From d821b7c24a09d6810665b03d9670909ff2c60665 Mon Sep 17 00:00:00 2001 From: Miguel Montalvo Date: Wed, 8 Feb 2023 15:51:45 -0800 Subject: [PATCH 1/7] Refactor modules plugin algorithm --- .../vite-plugin-shopify-modules/src/index.ts | 58 ++++++------------- 1 file changed, 19 insertions(+), 39 deletions(-) diff --git a/packages/vite-plugin-shopify-modules/src/index.ts b/packages/vite-plugin-shopify-modules/src/index.ts index 93f2e474..1477e1ef 100644 --- a/packages/vite-plugin-shopify-modules/src/index.ts +++ b/packages/vite-plugin-shopify-modules/src/index.ts @@ -1,7 +1,6 @@ import { promises as fs, existsSync } from 'fs' import path from 'path' import { Plugin, ResolvedConfig } from 'vite' -import { throttle } from 'lodash' import chokidar from 'chokidar' import glob from 'fast-glob' import createDebugger from 'debug' @@ -14,16 +13,6 @@ export default function shopifyModules (options: VitePluginShopifyModulesOptions const resolvedOptions = resolveOptions(options) let _config: ResolvedConfig - // Create throttled function for generating module symlinks - const linkModulesFn = throttle( - linkModules.bind(null, resolvedOptions), - 500, - { - leading: true, - trailing: false - } - ) - return { name: 'vite-plugin-shopify-modules', enforce: 'post', @@ -66,53 +55,44 @@ export default function shopifyModules (options: VitePluginShopifyModulesOptions } return null }, - buildStart: () => { + buildStart: async () => { + const modulePaths = await glob(`${resolvedOptions.modulesDir}/**/*.{section,snippet}.liquid`) + // Link modules on build start - linkModulesFn() + await Promise.all(modulePaths.map(async (modulePath) => { + return await linkModule(modulePath, resolvedOptions) + })) if (_config.command === 'serve') { // Watch for relevant file or directory changes to re-run script chokidar.watch([resolvedOptions.modulesDir, '(sections|snippets)/*.liquid'], { ignoreInitial: true, followSymlinks: false - }).on('all', linkModulesFn) + }).on('all', (event, path) => debug({ path })) } } } } // Check for module folders with corresponding liquid files and set up symlinks as needed -const linkModules = ({ modulesDir, themeRoot }: ResolvedVitePluginShopifyModulesOptions): void => { +const linkModule = async (modulePath: string, { modulesDir, themeRoot }: ResolvedVitePluginShopifyModulesOptions): Promise => { + const themeFilePath = getThemeFilePath(modulePath, { modulesDir, themeRoot }) + return await setupSymlink(modulePath, themeFilePath) +} + +const getThemeFilePath = (modulePath: string, { modulesDir, themeRoot }: ResolvedVitePluginShopifyModulesOptions): string => { const rootPath = path.resolve(themeRoot) const sectionsDir = path.resolve(rootPath, './sections') const snippetsDir = path.resolve(rootPath, './snippets') + const fileName = path.basename(modulePath) - if (existsSync(modulesDir)) { - fs.readdir(modulesDir) - .then( - async (modules: string[]) => await Promise.all(modules.flatMap((module) => [ - setupSectionSymlink(module, { modulesDir, sectionsDir }), - setupSnippetSymlink(module, { modulesDir, snippetsDir }) - ])), - (err) => { throw err } - ) + if (fileName.includes('.section.liquid')) { + const moduleName = fileName.replace(/\.section/, '') + return path.join(sectionsDir, `${moduleName}`) } -} - -// Set up symlink for module's liquid section file -const setupSectionSymlink = async (moduleName: string, pathConfig: { modulesDir: string, sectionsDir: string }): Promise => { - const moduleSectionPath = path.join(pathConfig.modulesDir, `${moduleName}/${moduleName}.section.liquid`) - const themeSectionPath = path.join(pathConfig.sectionsDir, `${moduleName}.liquid`) - - return await setupSymlink(moduleSectionPath, themeSectionPath) -} - -// Set up symlink for module's liquid snippet file -const setupSnippetSymlink = async (moduleName: string, pathConfig: { modulesDir: string, snippetsDir: string }): Promise => { - const moduleSnippetPath = path.join(pathConfig.modulesDir, `${moduleName}/${moduleName}.snippet.liquid`) - const themeSnippetPath = path.join(pathConfig.snippetsDir, `${moduleName}.liquid`) - return await setupSymlink(moduleSnippetPath, themeSnippetPath) + const moduleName = fileName.replace(/\.snippet/, '') + return path.join(snippetsDir, `${moduleName}`) } // Move liquid file from module path to theme path and generate symbolic link From 10a050c52fd2ba077f598d2ed7c878d257e2e361 Mon Sep 17 00:00:00 2001 From: Miguel Montalvo Date: Fri, 10 Feb 2023 16:05:22 -0800 Subject: [PATCH 2/7] Clean up --- .../vite-plugin-shopify-modules/README.md | 4 -- .../vite-plugin-shopify-modules/package.json | 3 +- .../vite-plugin-shopify-modules/src/index.ts | 53 +++++++++---------- pnpm-lock.yaml | 2 - 4 files changed, 26 insertions(+), 36 deletions(-) diff --git a/packages/vite-plugin-shopify-modules/README.md b/packages/vite-plugin-shopify-modules/README.md index cea8469b..1da786d8 100644 --- a/packages/vite-plugin-shopify-modules/README.md +++ b/packages/vite-plugin-shopify-modules/README.md @@ -111,10 +111,6 @@ See the [vite-plugin-shopify docs](https://github.com/barrel/barrel-shopify/tree See [seed-theme](https://github.com/barrel/barrel-shopify/tree/main/themes/seed-theme) for an example Shopify theme using this plugin. -## To-Do - -- [ ] Unit tests - ## Bugs Please create an issue if you found any bugs, to help us improve this project! diff --git a/packages/vite-plugin-shopify-modules/package.json b/packages/vite-plugin-shopify-modules/package.json index fd247d81..64dd5429 100644 --- a/packages/vite-plugin-shopify-modules/package.json +++ b/packages/vite-plugin-shopify-modules/package.json @@ -35,8 +35,7 @@ "dependencies": { "chokidar": "^3.5.3", "debug": "^4.3.4", - "fast-glob": "^3.2.11", - "lodash": "^4.17.21" + "fast-glob": "^3.2.11" }, "devDependencies": { "tsconfig": "workspace:*", diff --git a/packages/vite-plugin-shopify-modules/src/index.ts b/packages/vite-plugin-shopify-modules/src/index.ts index 1477e1ef..9456753a 100644 --- a/packages/vite-plugin-shopify-modules/src/index.ts +++ b/packages/vite-plugin-shopify-modules/src/index.ts @@ -1,6 +1,6 @@ -import { promises as fs, existsSync } from 'fs' +import { lstatSync, renameSync, unlinkSync, symlinkSync, existsSync, statSync } from 'fs' import path from 'path' -import { Plugin, ResolvedConfig } from 'vite' +import { Plugin } from 'vite' import chokidar from 'chokidar' import glob from 'fast-glob' import createDebugger from 'debug' @@ -11,7 +11,6 @@ const debug = createDebugger('vite-plugin-shopify:modules') export default function shopifyModules (options: VitePluginShopifyModulesOptions = {}): Plugin { const resolvedOptions = resolveOptions(options) - let _config: ResolvedConfig return { name: 'vite-plugin-shopify-modules', @@ -26,14 +25,11 @@ export default function shopifyModules (options: VitePluginShopifyModulesOptions } } }, - configResolved (config) { - _config = config - }, resolveId: async (id) => { // Check if path is within modules directory if (!path.relative(path.resolve(resolvedOptions.modulesDir), id).includes('..')) { try { - const fileStat = await fs.stat(id) + const fileStat = statSync(id) // Check if path refers to folder instead of file if (fileStat.isDirectory()) { @@ -55,29 +51,30 @@ export default function shopifyModules (options: VitePluginShopifyModulesOptions } return null }, - buildStart: async () => { + buildEnd: async () => { const modulePaths = await glob(`${resolvedOptions.modulesDir}/**/*.{section,snippet}.liquid`) // Link modules on build start - await Promise.all(modulePaths.map(async (modulePath) => { - return await linkModule(modulePath, resolvedOptions) - })) - - if (_config.command === 'serve') { - // Watch for relevant file or directory changes to re-run script - chokidar.watch([resolvedOptions.modulesDir, '(sections|snippets)/*.liquid'], { - ignoreInitial: true, - followSymlinks: false - }).on('all', (event, path) => debug({ path })) - } + modulePaths.forEach(modulePath => linkModule(modulePath, resolvedOptions)) + }, + configureServer () { + const watcher = chokidar.watch([resolvedOptions.modulesDir], { + ignoreInitial: true, + followSymlinks: false + }) + + // Watch for relevant file or directory changes to re-run script + watcher.on('add', path => linkModule(path, resolvedOptions)) + watcher.on('change', path => linkModule(path, resolvedOptions)) + watcher.on('unlink', path => linkModule(path, resolvedOptions)) } } } // Check for module folders with corresponding liquid files and set up symlinks as needed -const linkModule = async (modulePath: string, { modulesDir, themeRoot }: ResolvedVitePluginShopifyModulesOptions): Promise => { +const linkModule = (modulePath: string, { modulesDir, themeRoot }: ResolvedVitePluginShopifyModulesOptions): void => { const themeFilePath = getThemeFilePath(modulePath, { modulesDir, themeRoot }) - return await setupSymlink(modulePath, themeFilePath) + setupSymlink(modulePath, themeFilePath) } const getThemeFilePath = (modulePath: string, { modulesDir, themeRoot }: ResolvedVitePluginShopifyModulesOptions): string => { @@ -96,13 +93,13 @@ const getThemeFilePath = (modulePath: string, { modulesDir, themeRoot }: Resolve } // Move liquid file from module path to theme path and generate symbolic link -const setupSymlink = async (modulePath: string, themePath: string): Promise => { +const setupSymlink = (modulePath: string, themePath: string): void => { let modulePathStats debug({ modulePath, themePath }) try { - modulePathStats = await fs.lstat(modulePath) + modulePathStats = lstatSync(modulePath) } catch (e) { // } @@ -115,8 +112,8 @@ const setupSymlink = async (modulePath: string, themePath: string): Promise Date: Fri, 10 Feb 2023 16:15:05 -0800 Subject: [PATCH 3/7] Clean up types --- packages/vite-plugin-shopify-modules/src/index.ts | 6 +++--- packages/vite-plugin-shopify-modules/src/options.ts | 7 +------ 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/packages/vite-plugin-shopify-modules/src/index.ts b/packages/vite-plugin-shopify-modules/src/index.ts index 9456753a..3dc91299 100644 --- a/packages/vite-plugin-shopify-modules/src/index.ts +++ b/packages/vite-plugin-shopify-modules/src/index.ts @@ -5,7 +5,7 @@ import chokidar from 'chokidar' import glob from 'fast-glob' import createDebugger from 'debug' -import { VitePluginShopifyModulesOptions, ResolvedVitePluginShopifyModulesOptions, resolveOptions } from './options' +import { VitePluginShopifyModulesOptions, resolveOptions } from './options' const debug = createDebugger('vite-plugin-shopify:modules') @@ -72,12 +72,12 @@ export default function shopifyModules (options: VitePluginShopifyModulesOptions } // Check for module folders with corresponding liquid files and set up symlinks as needed -const linkModule = (modulePath: string, { modulesDir, themeRoot }: ResolvedVitePluginShopifyModulesOptions): void => { +const linkModule = (modulePath: string, { modulesDir, themeRoot }: Required): void => { const themeFilePath = getThemeFilePath(modulePath, { modulesDir, themeRoot }) setupSymlink(modulePath, themeFilePath) } -const getThemeFilePath = (modulePath: string, { modulesDir, themeRoot }: ResolvedVitePluginShopifyModulesOptions): string => { +const getThemeFilePath = (modulePath: string, { modulesDir, themeRoot }: Required): string => { const rootPath = path.resolve(themeRoot) const sectionsDir = path.resolve(rootPath, './sections') const snippetsDir = path.resolve(rootPath, './snippets') diff --git a/packages/vite-plugin-shopify-modules/src/options.ts b/packages/vite-plugin-shopify-modules/src/options.ts index 5c5e6bac..3c09388d 100644 --- a/packages/vite-plugin-shopify-modules/src/options.ts +++ b/packages/vite-plugin-shopify-modules/src/options.ts @@ -5,14 +5,9 @@ export interface VitePluginShopifyModulesOptions { themeRoot?: string } -export interface ResolvedVitePluginShopifyModulesOptions { - modulesDir: string - themeRoot: string -} - export const resolveOptions = ( options: VitePluginShopifyModulesOptions -): ResolvedVitePluginShopifyModulesOptions => ({ +): Required => ({ modulesDir: typeof options.modulesDir !== 'undefined' ? path.normalize(options.modulesDir) : 'modules', themeRoot: typeof options.themeRoot !== 'undefined' ? path.normalize(options.themeRoot) : './' }) From 31c546942216c135e176f2b447642071a9650b1e Mon Sep 17 00:00:00 2001 From: Miguel Montalvo Date: Mon, 13 Feb 2023 11:26:16 -0800 Subject: [PATCH 4/7] Normalize paths to use POSIX separators --- packages/vite-plugin-shopify-modules/src/index.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/vite-plugin-shopify-modules/src/index.ts b/packages/vite-plugin-shopify-modules/src/index.ts index 3dc91299..b7a96389 100644 --- a/packages/vite-plugin-shopify-modules/src/index.ts +++ b/packages/vite-plugin-shopify-modules/src/index.ts @@ -1,6 +1,6 @@ import { lstatSync, renameSync, unlinkSync, symlinkSync, existsSync, statSync } from 'fs' import path from 'path' -import { Plugin } from 'vite' +import { Plugin, normalizePath } from 'vite' import chokidar from 'chokidar' import glob from 'fast-glob' import createDebugger from 'debug' @@ -52,7 +52,7 @@ export default function shopifyModules (options: VitePluginShopifyModulesOptions return null }, buildEnd: async () => { - const modulePaths = await glob(`${resolvedOptions.modulesDir}/**/*.{section,snippet}.liquid`) + const modulePaths = await glob(`${normalizePath(resolvedOptions.modulesDir)}/**/*.{section,snippet}.liquid`) // Link modules on build start modulePaths.forEach(modulePath => linkModule(modulePath, resolvedOptions)) From c733582ea0ceb91b063d9361b9b62b23366c6f70 Mon Sep 17 00:00:00 2001 From: Miguel Montalvo Date: Mon, 13 Feb 2023 11:43:35 -0800 Subject: [PATCH 5/7] Watch liquid files from modules only --- packages/vite-plugin-shopify-modules/src/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/vite-plugin-shopify-modules/src/index.ts b/packages/vite-plugin-shopify-modules/src/index.ts index b7a96389..b9b69d5c 100644 --- a/packages/vite-plugin-shopify-modules/src/index.ts +++ b/packages/vite-plugin-shopify-modules/src/index.ts @@ -58,7 +58,7 @@ export default function shopifyModules (options: VitePluginShopifyModulesOptions modulePaths.forEach(modulePath => linkModule(modulePath, resolvedOptions)) }, configureServer () { - const watcher = chokidar.watch([resolvedOptions.modulesDir], { + const watcher = chokidar.watch([`${resolvedOptions.modulesDir}/**/*.liquid`], { ignoreInitial: true, followSymlinks: false }) From 67860fe47d3670d225b01310d28bdcbbb0f24ae5 Mon Sep 17 00:00:00 2001 From: Miguel Montalvo Date: Mon, 13 Feb 2023 12:36:48 -0800 Subject: [PATCH 6/7] Test that module file names and module directories precisely match --- packages/vite-plugin-shopify-modules/src/index.ts | 14 +++++++++++--- .../modules/collage/foo.snippet.liquid | 0 .../vite-plugin-shopify-modules/test/index.test.ts | 1 + 3 files changed, 12 insertions(+), 3 deletions(-) create mode 100644 packages/vite-plugin-shopify-modules/test/__fixtures__/modules/collage/foo.snippet.liquid diff --git a/packages/vite-plugin-shopify-modules/src/index.ts b/packages/vite-plugin-shopify-modules/src/index.ts index b9b69d5c..418e324a 100644 --- a/packages/vite-plugin-shopify-modules/src/index.ts +++ b/packages/vite-plugin-shopify-modules/src/index.ts @@ -52,7 +52,7 @@ export default function shopifyModules (options: VitePluginShopifyModulesOptions return null }, buildEnd: async () => { - const modulePaths = await glob(`${normalizePath(resolvedOptions.modulesDir)}/**/*.{section,snippet}.liquid`) + const modulePaths = await glob(`${normalizePath(resolvedOptions.modulesDir)}/**/*.liquid`) // Link modules on build start modulePaths.forEach(modulePath => linkModule(modulePath, resolvedOptions)) @@ -83,7 +83,7 @@ const getThemeFilePath = (modulePath: string, { modulesDir, themeRoot }: Require const snippetsDir = path.resolve(rootPath, './snippets') const fileName = path.basename(modulePath) - if (fileName.includes('.section.liquid')) { + if (/\.section.liquid/.test(fileName)) { const moduleName = fileName.replace(/\.section/, '') return path.join(sectionsDir, `${moduleName}`) } @@ -94,9 +94,17 @@ const getThemeFilePath = (modulePath: string, { modulesDir, themeRoot }: Require // Move liquid file from module path to theme path and generate symbolic link const setupSymlink = (modulePath: string, themePath: string): void => { + const moduleDir = path.basename(path.dirname(modulePath)) let modulePathStats + const fileName = path.basename(modulePath) + const regex = new RegExp(moduleDir) + const isPotentialModuleName = regex.test(fileName) + + debug({ modulePath, themePath, moduleDir, fileName }) - debug({ modulePath, themePath }) + if (!isPotentialModuleName) { + return + } try { modulePathStats = lstatSync(modulePath) diff --git a/packages/vite-plugin-shopify-modules/test/__fixtures__/modules/collage/foo.snippet.liquid b/packages/vite-plugin-shopify-modules/test/__fixtures__/modules/collage/foo.snippet.liquid new file mode 100644 index 00000000..e69de29b diff --git a/packages/vite-plugin-shopify-modules/test/index.test.ts b/packages/vite-plugin-shopify-modules/test/index.test.ts index 61ef1aea..4b111b44 100644 --- a/packages/vite-plugin-shopify-modules/test/index.test.ts +++ b/packages/vite-plugin-shopify-modules/test/index.test.ts @@ -33,6 +33,7 @@ describe('vite-plugin-shopify:modules', () => { expect(existsSync(path.join(__dirname, '__fixtures__', 'snippets', 'multirow.liquid'))).toEqual(true) expect(existsSync(path.join(__dirname, '__fixtures__', 'sections', 'multirow.liquid'))).toEqual(true) + expect(existsSync(path.join(__dirname, '__fixtures__', 'sections', 'foo.liquid'))).toEqual(false) expect(lstatSync(path.join(__dirname, '__fixtures__', 'modules', 'multirow', 'multirow.section.liquid')).isSymbolicLink()).toEqual(true) expect(lstatSync(path.join(__dirname, '__fixtures__', 'modules', 'multirow', 'multirow.snippet.liquid')).isSymbolicLink()).toEqual(true) expect(lstatSync(path.join(__dirname, '__fixtures__', 'modules', 'collage', 'collage.section.liquid')).isSymbolicLink()).toEqual(true) From 3e4e48d9bf1891724af2250b3b05b398da6d7997 Mon Sep 17 00:00:00 2001 From: Miguel Montalvo Date: Mon, 13 Feb 2023 13:24:51 -0800 Subject: [PATCH 7/7] Update comment --- packages/vite-plugin-shopify-modules/src/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/vite-plugin-shopify-modules/src/index.ts b/packages/vite-plugin-shopify-modules/src/index.ts index 418e324a..a110ba34 100644 --- a/packages/vite-plugin-shopify-modules/src/index.ts +++ b/packages/vite-plugin-shopify-modules/src/index.ts @@ -54,7 +54,7 @@ export default function shopifyModules (options: VitePluginShopifyModulesOptions buildEnd: async () => { const modulePaths = await glob(`${normalizePath(resolvedOptions.modulesDir)}/**/*.liquid`) - // Link modules on build start + // Link modules on build end modulePaths.forEach(modulePath => linkModule(modulePath, resolvedOptions)) }, configureServer () {