Skip to content

Commit

Permalink
Merge pull request #43 from barrel/refactor-modules-plugin
Browse files Browse the repository at this point in the history
vite-plugin-shopify-modules: Refactor
  • Loading branch information
montalvomiguelo authored Feb 13, 2023
2 parents a58902c + 3e4e48d commit 84ed487
Show file tree
Hide file tree
Showing 7 changed files with 51 additions and 77 deletions.
4 changes: 0 additions & 4 deletions packages/vite-plugin-shopify-modules/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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!
3 changes: 1 addition & 2 deletions packages/vite-plugin-shopify-modules/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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:*",
Expand Down
111 changes: 48 additions & 63 deletions packages/vite-plugin-shopify-modules/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,28 +1,16 @@
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 { throttle } from 'lodash'
import { Plugin, normalizePath } from 'vite'
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')

export default function shopifyModules (options: VitePluginShopifyModulesOptions = {}): Plugin {
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',
Expand All @@ -37,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()) {
Expand All @@ -66,63 +51,63 @@ export default function shopifyModules (options: VitePluginShopifyModulesOptions
}
return null
},
buildStart: () => {
// Link modules on build start
linkModulesFn()

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)
}
buildEnd: async () => {
const modulePaths = await glob(`${normalizePath(resolvedOptions.modulesDir)}/**/*.liquid`)

// Link modules on build end
modulePaths.forEach(modulePath => linkModule(modulePath, resolvedOptions))
},
configureServer () {
const watcher = chokidar.watch([`${resolvedOptions.modulesDir}/**/*.liquid`], {
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 linkModules = ({ modulesDir, themeRoot }: ResolvedVitePluginShopifyModulesOptions): void => {
const linkModule = (modulePath: string, { modulesDir, themeRoot }: Required<VitePluginShopifyModulesOptions>): void => {
const themeFilePath = getThemeFilePath(modulePath, { modulesDir, themeRoot })
setupSymlink(modulePath, themeFilePath)
}

const getThemeFilePath = (modulePath: string, { modulesDir, themeRoot }: Required<VitePluginShopifyModulesOptions>): 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 (/\.section.liquid/.test(fileName)) {
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<void> => {
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<void> => {
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
const setupSymlink = async (modulePath: string, themePath: string): Promise<void> => {
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 })
debug({ modulePath, themePath, moduleDir, fileName })

if (!isPotentialModuleName) {
return
}

try {
modulePathStats = await fs.lstat(modulePath)
modulePathStats = lstatSync(modulePath)
} catch (e) {
//
}
Expand All @@ -135,8 +120,8 @@ const setupSymlink = async (modulePath: string, themePath: string): Promise<void
if (existsSync(themePath)) {
if (!modulePathStats.isSymbolicLink()) {
// If theme file exists but hasn't been linked, create symlink
await fs.unlink(modulePath)
await fs.symlink(path.relative(path.dirname(modulePath), themePath), modulePath)
unlinkSync(modulePath)
symlinkSync(path.relative(path.dirname(modulePath), themePath), modulePath)
}

// If theme path file already exists, skip
Expand All @@ -146,16 +131,16 @@ const setupSymlink = async (modulePath: string, themePath: string): Promise<void
if (modulePathStats.isSymbolicLink()) {
if (!existsSync(themePath)) {
// If symlink exists without target file, delete it
await fs.unlink(modulePath)
unlinkSync(modulePath)
}

// If module path file is already a symlink, skip
return
}

// Move liquid file to theme folder
await fs.rename(modulePath, themePath)
renameSync(modulePath, themePath)

// Generate symlink from module path to theme path
await fs.symlink(path.relative(path.dirname(modulePath), themePath), modulePath)
symlinkSync(path.relative(path.dirname(modulePath), themePath), modulePath)
}
7 changes: 1 addition & 6 deletions packages/vite-plugin-shopify-modules/src/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,9 @@ export interface VitePluginShopifyModulesOptions {
themeRoot?: string
}

export interface ResolvedVitePluginShopifyModulesOptions {
modulesDir: string
themeRoot: string
}

export const resolveOptions = (
options: VitePluginShopifyModulesOptions
): ResolvedVitePluginShopifyModulesOptions => ({
): Required<VitePluginShopifyModulesOptions> => ({
modulesDir: typeof options.modulesDir !== 'undefined' ? path.normalize(options.modulesDir) : 'modules',
themeRoot: typeof options.themeRoot !== 'undefined' ? path.normalize(options.themeRoot) : './'
})
Empty file.
1 change: 1 addition & 0 deletions packages/vite-plugin-shopify-modules/test/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
2 changes: 0 additions & 2 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 84ed487

Please sign in to comment.