Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/silly-crews-smile.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"vite-plugin-shopify": minor
---

Add snippetAssetFile option for URL-only output
7 changes: 7 additions & 0 deletions docs/guide/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,13 @@ Additional files to use as entry points (accepts an array of file paths or glob

Specifies the file name of the snippet that loads your assets.

## snippetAssetFile

- **Type:** `boolean | string`
- **Default:** `false`

This snippet outputs the URL for the given entrypoint.

## versionNumbers

- **Type:** `boolean`
Expand Down
1 change: 1 addition & 0 deletions examples/vite-shopify-example/.gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
/assets
/snippets/vite.liquid
/snippets/vite-asset.liquid
3 changes: 2 additions & 1 deletion examples/vite-shopify-example/layout/theme.liquid
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,9 @@

<meta name="description" content="">

<link rel="preload" href="{%- render 'vite-asset', entry: 'theme.scss' -%}" as="style">
{%- liquid
render 'vite' with 'theme.scss', preload_stylesheet: true
render 'vite' with 'theme.scss'
render 'vite' with 'theme.ts'
render 'vite' with '@/foo.ts'
render 'vite' with '~/bar.ts'
Expand Down
1 change: 1 addition & 0 deletions examples/vite-shopify-example/vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export default defineConfig({
shopify({
tunnel: true,
snippetFile: 'vite.liquid',
snippetAssetFile: 'vite-asset.liquid',
additionalEntrypoints: [
'frontend/foo.ts', // relative to sourceCodeDir
'frontend/bar.ts',
Expand Down
4 changes: 4 additions & 0 deletions packages/vite-plugin-shopify/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ export default {
additionalEntrypoints: [],
// Specifies the file name of the snippet that loads your assets
snippetFile: 'vite-tag.liquid',
// This snippet outputs the URL for the given entrypoint
snippetAssetFile: false,
// Specifies whether to append version numbers to your production-ready asset URLs in `snippetFile`
versionNumbers: false,
// Enables the creation of Cloudflare tunnels during dev, allowing previews from any device
Expand Down Expand Up @@ -127,6 +129,8 @@ You can pass the `preload_stylesheet` variable to the `vite-tag` snippet to enab
{% render 'vite-tag' with 'theme.scss', preload_stylesheet: true %}
```

Alternatively, use `snippetAssetFile: 'vite-asset.liquid'` to get just the URL: `{% render 'vite-asset', entry: 'theme.scss' %}`

### Import aliases

For convenience, `~/` and `@/` are aliased to your `frontend` folder, which simplifies imports:
Expand Down
2 changes: 2 additions & 0 deletions packages/vite-plugin-shopify/src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,5 @@ export const CSS_EXTENSIONS_REGEX = new RegExp(
export const hotReloadScriptId = 'hot-reload-client'

export const hotReloadScriptUrl = 'https://cdn.jsdelivr.net/npm/@shopify/theme-hot-reload/dist/theme-hot-reload.min.js'

export const snippetAssetFile = 'vite-asset.liquid'
50 changes: 47 additions & 3 deletions packages/vite-plugin-shopify/src/html.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import createDebugger from 'debug'
import startTunnel from '@shopify/plugin-cloudflare/hooks/tunnel'
import { renderInfo, isTTY } from '@shopify/cli-kit/node/ui'

import { CSS_EXTENSIONS_REGEX, KNOWN_CSS_EXTENSIONS, hotReloadScriptId, hotReloadScriptUrl } from './constants'
import { CSS_EXTENSIONS_REGEX, KNOWN_CSS_EXTENSIONS, hotReloadScriptId, hotReloadScriptUrl, snippetAssetFile } from './constants'
import type { Options, DevServerUrl, FrontendURLResult } from './types'
import type { TunnelClient } from '@shopify/cli-kit/node/plugins/tunnel'

Expand All @@ -21,8 +21,20 @@ export default function shopifyHTML (options: Required<Options>): Plugin {

const viteTagSnippetPath = path.resolve(options.themeRoot, `snippets/${options.snippetFile}`)
const viteTagSnippetName = options.snippetFile.replace(/\.[^.]+$/, '')
const viteTagSnippetPrefix = (config: ResolvedConfig): string =>
viteTagDisclaimer + viteTagEntryPath(config.resolve.alias, options.entrypointsDir, viteTagSnippetName)
const viteTagSnippetPrefix = (config: ResolvedConfig, snippetName = viteTagSnippetName): string =>
viteTagDisclaimer + viteTagEntryPath(config.resolve.alias, options.entrypointsDir, snippetName)

const resolvedSnippetAssetFile = typeof options.snippetAssetFile === 'string'
? options.snippetAssetFile
: options.snippetAssetFile
? snippetAssetFile
: ''
const viteAssetSnippetPath = resolvedSnippetAssetFile
? path.resolve(options.themeRoot, `snippets/${resolvedSnippetAssetFile}`)
: ''
const viteAssetSnippetName = resolvedSnippetAssetFile
? path.parse(resolvedSnippetAssetFile).name
: ''

return {
name: 'vite-plugin-shopify-html',
Expand Down Expand Up @@ -76,8 +88,14 @@ export default function shopifyHTML (options: Required<Options>): Plugin {
tunnelUrl, options.entrypointsDir, reactPlugin, options.themeHotReload
)

const viteAssetSnippetContent = viteAssetSnippetName && (viteTagSnippetPrefix(config, viteAssetSnippetName) + viteAssetSnippetDev(
tunnelUrl, options.entrypointsDir
))

// Write vite-tag with a Cloudflare Tunnel URL
fs.writeFileSync(viteTagSnippetPath, viteTagSnippetContent)

viteAssetSnippetContent && fs.writeFileSync(viteAssetSnippetPath, viteAssetSnippetContent)
})()
}, 100)

Expand All @@ -87,8 +105,14 @@ export default function shopifyHTML (options: Required<Options>): Plugin {
: viteDevServerUrl, options.entrypointsDir, reactPlugin, options.themeHotReload
)

const viteAssetSnippetContent = viteAssetSnippetName && (viteTagSnippetPrefix(config, viteAssetSnippetName) + viteAssetSnippetDev(
frontendUrl !== '' ? frontendUrl : viteDevServerUrl, options.entrypointsDir
))

// Write vite-tag snippet for development server
fs.writeFileSync(viteTagSnippetPath, viteTagSnippetContent)

viteAssetSnippetContent && fs.writeFileSync(viteAssetSnippetPath, viteAssetSnippetContent)
}
})

Expand Down Expand Up @@ -125,6 +149,7 @@ export default function shopifyHTML (options: Required<Options>): Plugin {
}

const assetTags: string[] = []
const assetUrls: string[] = []
const manifest = JSON.parse(
fs.readFileSync(manifestFilePath, 'utf8')
) as Manifest
Expand Down Expand Up @@ -174,18 +199,24 @@ export default function shopifyHTML (options: Required<Options>): Plugin {
}

assetTags.push(viteEntryTag(entryPaths, tagsForEntry.join('\n '), assetTags.length === 0))
assetUrls.push(viteEntryTag(entryPaths, `{{ ${assetUrl(file, options.versionNumbers)} }}`, assetUrls.length === 0))
}

// Generate entry tag for bundled "style.css" file when cssCodeSplit is false
if (src === 'style.css' && !config.build.cssCodeSplit) {
assetTags.push(viteEntryTag([src], stylesheetTag(file, options.versionNumbers), false))
assetUrls.push(viteEntryTag([src], `{{ ${assetUrl(file, options.versionNumbers)} }}`, false))
}
})

const viteTagSnippetContent = viteTagSnippetPrefix(config) + assetTags.join('\n') + '\n{% endif %}\n'

const viteAssetSnippetContent = viteAssetSnippetName && viteTagSnippetPrefix(config, viteAssetSnippetName) + assetUrls.join('\n') + '\n{% endif %}\n'

// Write vite-tag snippet for production build
fs.writeFileSync(viteTagSnippetPath, viteTagSnippetContent)

viteAssetSnippetContent && fs.writeFileSync(viteAssetSnippetPath, viteAssetSnippetContent)
}
}
}
Expand Down Expand Up @@ -245,6 +276,19 @@ const scriptTag = (fileName: string, versionNumbers: boolean): string =>
const stylesheetTag = (fileName: string, versionNumbers: boolean): string =>
`{{ ${assetUrl(fileName, versionNumbers)} | stylesheet_tag: preload: preload_stylesheet }}`

// Generate vite-asset snippet for development
const viteAssetSnippetDev = (assetHost: string, entrypointsDir: string): string =>
`{% liquid
assign path_prefix = path | slice: 0
if path_prefix == '/'
assign file_url_prefix = '${assetHost}'
else
assign file_url_prefix = '${assetHost}/${entrypointsDir}/'
endif
assign file_url = path | prepend: file_url_prefix
echo file_url
%}`

// Generate vite-tag snippet for development
const viteTagSnippetDev = (assetHost: string, entrypointsDir: string, reactPlugin: Plugin | undefined, themeHotReload: boolean): string =>
`{% liquid
Expand Down
2 changes: 2 additions & 0 deletions packages/vite-plugin-shopify/src/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export const resolveOptions = (
const entrypointsDir = options.entrypointsDir ?? normalizePath(path.join(sourceCodeDir, 'entrypoints'))
const additionalEntrypoints = options.additionalEntrypoints ?? []
const snippetFile = options.snippetFile ?? 'vite-tag.liquid'
const snippetAssetFile = options.snippetAssetFile ?? false
const versionNumbers = options.versionNumbers ?? false
const tunnel = options.tunnel ?? false
const themeHotReload = options.themeHotReload ?? true
Expand All @@ -20,6 +21,7 @@ export const resolveOptions = (
entrypointsDir,
additionalEntrypoints,
snippetFile,
snippetAssetFile,
versionNumbers,
tunnel,
themeHotReload
Expand Down
7 changes: 7 additions & 0 deletions packages/vite-plugin-shopify/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,13 @@ export interface Options {
*/
snippetFile?: string

/**
* This snippet outputs the URL for the given entrypoint.
*
* @default false
*/
snippetAssetFile?: boolean | string

/**
* Specifies whether to append version numbers to your production-ready asset URLs in {@link snippetFile}.
*
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{% comment %}
IMPORTANT: This snippet is automatically generated by vite-plugin-shopify.
Do not attempt to modify this file directly, as any changes will be overwritten by the next build.
{% endcomment %}
{% liquid
assign entry = entry | default: vite-asset
assign path = entry | replace: '@@/', '../../resources/js/' | replace: '~/', '../' | replace: '@/', '../'
%}
{% if path == "/test/__fixtures__/frontend/entrypoints/customers.js" or path == "customers.js" %}
{{ 'customers-MM9Bv3NP.js' | asset_url }}
{% elsif path == "/test/__fixtures__/frontend/entrypoints/theme.css" or path == "theme.css" %}
{{ 'theme-y6Yj_vm2.css' | asset_url }}
{% elsif path == "/test/__fixtures__/frontend/entrypoints/theme.js" or path == "theme.js" %}
{{ 'theme-Df-cUSaP.js' | asset_url }}
{% endif %}
21 changes: 21 additions & 0 deletions packages/vite-plugin-shopify/test/__snapshots__/html.test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -184,3 +184,24 @@ exports[`vite-plugin-shopify:html > builds out .liquid files for development wit
{% endif %}
"
`;

exports[`vite-plugin-shopify:html > builds out vite-asset snippet for development 1`] = `
"{% comment %}
IMPORTANT: This snippet is automatically generated by vite-plugin-shopify.
Do not attempt to modify this file directly, as any changes will be overwritten by the next build.
{% endcomment %}
{% liquid
assign entry = entry | default: vite-asset
assign path = entry | replace: '~/', '../' | replace: '@/', '../' | replace: '@@/', '../../resources/js/'
%}
{% liquid
assign path_prefix = path | slice: 0
if path_prefix == '/'
assign file_url_prefix = 'http://localhost:5173'
else
assign file_url_prefix = 'http://localhost:5173/test/__fixtures__/frontend/entrypoints/'
endif
assign file_url = path | prepend: file_url_prefix
echo file_url
%}"
`;
38 changes: 38 additions & 0 deletions packages/vite-plugin-shopify/test/__snapshots__/index.test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -68,3 +68,41 @@ exports[`vite-plugin-shopify > builds out .liquid files for production without m
{% endif %}
"
`;

exports[`vite-plugin-shopify > builds out vite-asset snippet for production 1`] = `
"{% comment %}
IMPORTANT: This snippet is automatically generated by vite-plugin-shopify.
Do not attempt to modify this file directly, as any changes will be overwritten by the next build.
{% endcomment %}
{% liquid
assign entry = entry | default: vite-asset
assign path = entry | replace: '@@/', '../../resources/js/' | replace: '~/', '../' | replace: '@/', '../'
%}
{% if path == "/test/__fixtures__/frontend/entrypoints/customers.js" or path == "customers.js" %}
{{ 'customers-MM9Bv3NP.js' | asset_url | split: '?' | first }}
{% elsif path == "/test/__fixtures__/frontend/entrypoints/theme.css" or path == "theme.css" %}
{{ 'theme-y6Yj_vm2.css' | asset_url | split: '?' | first }}
{% elsif path == "/test/__fixtures__/frontend/entrypoints/theme.js" or path == "theme.js" %}
{{ 'theme-Df-cUSaP.js' | asset_url | split: '?' | first }}
{% endif %}
"
`;

exports[`vite-plugin-shopify > builds out vite-asset snippet for production with version numbers 1`] = `
"{% comment %}
IMPORTANT: This snippet is automatically generated by vite-plugin-shopify.
Do not attempt to modify this file directly, as any changes will be overwritten by the next build.
{% endcomment %}
{% liquid
assign entry = entry | default: vite-asset
assign path = entry | replace: '@@/', '../../resources/js/' | replace: '~/', '../' | replace: '@/', '../'
%}
{% if path == "/test/__fixtures__/frontend/entrypoints/customers.js" or path == "customers.js" %}
{{ 'customers-MM9Bv3NP.js' | asset_url }}
{% elsif path == "/test/__fixtures__/frontend/entrypoints/theme.css" or path == "theme.css" %}
{{ 'theme-y6Yj_vm2.css' | asset_url }}
{% elsif path == "/test/__fixtures__/frontend/entrypoints/theme.js" or path == "theme.js" %}
{{ 'theme-Df-cUSaP.js' | asset_url }}
{% endif %}
"
`;
5 changes: 4 additions & 1 deletion packages/vite-plugin-shopify/test/config.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,17 +120,20 @@ describe('resolveOptions', () => {
expect(options.entrypointsDir).toBe('frontend/entrypoints')
expect(options.additionalEntrypoints).toEqual([])
expect(options.snippetFile).toEqual('vite-tag.liquid')
expect(options.snippetAssetFile).toBe(false)
})

it('accepts a partial configuration', () => {
const options = resolveOptions({
themeRoot: 'shopify',
sourceCodeDir: 'src'
sourceCodeDir: 'src',
snippetAssetFile: true
})

expect(options.themeRoot).toBe('shopify')
expect(options.sourceCodeDir).toBe('src')
expect(options.entrypointsDir).toBe('src/entrypoints')
expect(options.snippetAssetFile).toBe(true)
})
})

Expand Down
27 changes: 27 additions & 0 deletions packages/vite-plugin-shopify/test/html.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -193,4 +193,31 @@ describe('vite-plugin-shopify:html', () => {

vi.useRealTimers()
})

it('builds out vite-asset snippet for development', async () => {
const options = resolveOptions({
themeRoot: 'test/__fixtures__',
sourceCodeDir: 'test/__fixtures__/frontend',
snippetAssetFile: 'vite-asset.liquid'
})

const { configureServer } = html(options)

const viteServer = await (
await createServer({
logLevel: 'silent',
configFile: path.join(__dirname, '__fixtures__', 'vite.config.js')
})
).listen()

configureServer(viteServer)

viteServer.httpServer?.emit('listening')

const assetSnippet = await fs.readFile(path.join(__dirname, '__fixtures__', 'snippets', 'vite-asset.liquid'), { encoding: 'utf8' })

await viteServer.close()

expect(assetSnippet).toMatchSnapshot()
})
})
Loading