diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..0718e1b --- /dev/null +++ b/.editorconfig @@ -0,0 +1,18 @@ +root=true + +[*] +end_of_line = lf +insert_final_newline = true +charset = utf-8 +indent_style = tab +trim_trailing_whitespace = true + +[{package.json,*.yml,*.yaml}] +indent_style = space +indent_size = 2 + +[Dockerfile] +indent_style = tab + +[Makefile] +indent_style = tab diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 0000000..1ba3db8 --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,71 @@ +const defaultRules = { + // No console statements in production + 'no-console': process.env.NODE_ENV !== 'development' ? 'error' : 'off', + // No debugger statements in production + 'no-debugger': process.env.NODE_ENV !== 'development' ? 'error' : 'off', + // Enforce prettier formatting + 'prettier/prettier': 'error', +}; + +module.exports = { + // Stop looking for ESLint configurations in parent folders + root: true, + // Global variables: Browser and Node.js + env: { + browser: true, + node: true, + }, + // Basic configuration for js files + plugins: ['@typescript-eslint', 'prettier'], + extends: ['eslint:recommended', 'prettier'], + rules: defaultRules, + parserOptions: { + ecmaVersion: 2020, + }, + overrides: [ + // Parse rollup configuration as module + { + files: ['rollup.config.js', 'vite.config.js'], + parserOptions: { + sourceType: 'module', + }, + }, + { + files: ['**/*.test.js'], + env: { + jest: true, + }, + plugins: ['jest'], + }, + // Configuration for ts/vue files + { + files: ['*.ts', '*.vue'], + parser: 'vue-eslint-parser', + parserOptions: { + parser: '@typescript-eslint/parser', + }, + extends: [ + 'plugin:vue/vue3-recommended', + 'eslint:recommended', + 'plugin:@typescript-eslint/recommended', + 'prettier', + ], + rules: { + ...defaultRules, + 'vue/multi-word-component-names': 'off', + // It's recommended to turn off this rule on TypeScript projects + 'no-undef': 'off', + // Allow ts-directive comments (used to suppress TypeScript compiler errors) + '@typescript-eslint/ban-ts-comment': 'off', + // Allow usage of the any type (consider to enable this rule later on) + '@typescript-eslint/no-explicit-any': 'off', + // Allow usage of require statements (consider to enable this rule later on) + '@typescript-eslint/no-var-requires': 'off', + // Allow non-null assertions for now (consider to enable this rule later on) + '@typescript-eslint/no-non-null-assertion': 'off', + // Allow unused arguments and variables when they begin with an underscore + '@typescript-eslint/no-unused-vars': ['warn', { argsIgnorePattern: '^_', varsIgnorePattern: '^_' }], + }, + }, + ], +}; diff --git a/.gitignore b/.gitignore index 6423b8b..b5bbbe7 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ .DS_Store node_modules .vscode +yarn.lock diff --git a/.prettierrc.js b/.prettierrc.js new file mode 100644 index 0000000..758f6fe --- /dev/null +++ b/.prettierrc.js @@ -0,0 +1,7 @@ +module.exports = { + htmlWhitespaceSensitivity: 'ignore', + printWidth: 120, + singleQuote: true, + useTabs: true, + proseWrap: 'always', +}; diff --git a/gatsby-node.js b/gatsby-node.js index dbfa74e..042f8f6 100644 --- a/gatsby-node.js +++ b/gatsby-node.js @@ -35,14 +35,17 @@ exports.pluginOptionsSchema = ({ Joi }) => { /** * Gatsby source implementation. */ + exports.sourceNodes = async (gatsbyOptions, pluginOptions) => { + const { actions: { createNode }, createNodeId, store, cache, reporter } = gatsbyOptions + const { headers } = await plugin.getOptions(); + const { Authorization } = await headers(); + await plugin.setOptions(pluginOptions); const optionsSystem = plugin.getOptionsSystem(); const options = plugin.getOptions(); - const createNode = gatsbyOptions.actions.createNode; - // Avoid type conflict with gatsby-source-graphql gatsbyOptions.actions.createNode = (node) => { if (node.internal.type === 'GraphQLSource') { @@ -55,6 +58,34 @@ exports.sourceNodes = async (gatsbyOptions, pluginOptions) => { await sourceNodes(gatsbyOptions, optionsSystem); await sourceNodes(gatsbyOptions, options); + + // Load images here rather than on file resolution. + // Create a node for each image and store it in the cache + // so it can bre retrieved on file resolution. + const remoteImages = await plugin.getAllImages(); + remoteImages.forEach(async (image) => { + const cachedImage = await cache.get(image.id) + if (cachedImage !== undefined) { + return + } + const nameParts = image.filename_download.split('.'); + const ext = nameParts.length > 1 ? `.${nameParts.pop()}` : ''; + const name = nameParts.join('.'); + const imageUrl = `${plugin.url}assets/${image.id}`; + const img = await createRemoteFileNode({ + url: imageUrl, + parentNodeId: image.id, + store, + cache, + createNode, + createNodeId, + httpHeaders: { Authorization }, + reporter, + ext, + name, + }); + await cache.set(image.id, img); + }) }; exports.createSchemaCustomization = async (gatsby, pluginOptions) => { @@ -67,44 +98,16 @@ exports.createSchemaCustomization = async (gatsby, pluginOptions) => { /** * Gatsby file implementation. */ -exports.createResolvers = async ({ actions, cache, createNodeId, createResolvers, store, reporter }, pluginOptions) => { +exports.createResolvers = async ({ cache, createResolvers }, pluginOptions) => { await plugin.setOptions(pluginOptions); - const { createNode } = actions; - - const { headers } = await plugin.getOptions(); - const { Authorization } = await headers(); - const fileResolver = { imageFile: { type: `File`, async resolve(source) { - if (!source || !source.id) return null; - - let filename_download = plugin.fileCache.get(source.id); - - if (!filename_download) { - if (source.filename_download) filename_download = source.filename_download; - else ({ filename_download } = await plugin.directus.files.readOne(source.id)); - - plugin.fileCache.set(source.id, filename_download); - } - - const nameParts = filename_download.split('.'); - const ext = nameParts.length > 1 ? `.${nameParts.pop()}` : ''; - const name = nameParts.join('.'); - - return createRemoteFileNode({ - url: `${plugin.url}assets/${source.id}`, - store, - cache, - createNode, - createNodeId, - httpHeaders: { Authorization }, - reporter, - ext, - name, - }); + // Lookup the cached image node and return it + const cachedFile = await cache.get(source.id); + return cachedFile; }, }, }; @@ -208,6 +211,15 @@ class Plugin { }; } + /** + * Method to retrieve all of the images in directus.files + */ + async getAllImages() { + const files = await this.directus.files.readByQuery({ limit: -1 }); + const imageFiles = files.data.filter(file => file.type.indexOf('image') > -1); + return imageFiles; + } + async headers() { let headers = {}; if (typeof this.options?.headers === 'object') { diff --git a/package.json b/package.json index 6bb6422..5819517 100644 --- a/package.json +++ b/package.json @@ -19,5 +19,8 @@ "bugs": { "url": "https://github.com/directus/directus/issues" }, - "gitHead": "24621f3934dc77eb23441331040ed13c676ceffd" + "gitHead": "24621f3934dc77eb23441331040ed13c676ceffd", + "devDependencies": { + "eslint-config-prettier": "^8.5.0" + } }