diff --git a/packages/directory-plugin/src/index.js b/packages/directory-plugin/src/index.js index e9ae2eb04..8c1b06fc6 100644 --- a/packages/directory-plugin/src/index.js +++ b/packages/directory-plugin/src/index.js @@ -1,8 +1,7 @@ import { promises as fs } from 'fs'; +import { normalizePath } from 'wmr'; import path from 'path'; -const pathToPosix = p => p.split(path.sep).join(path.posix.sep); - /** * @param {object} [options] * @param {string} [options.cwd] @@ -17,7 +16,7 @@ function directoryPlugin(options) { const resolved = await this.resolve(id.slice(4) + '\0', importer, { skipSelf: true }); if (resolved) { - return '\0dir:' + pathToPosix(resolved.id).replace(/\0$/, ''); + return '\0dir:' + normalizePath(resolved.id).replace(/\0$/, ''); } }, async load(id) { diff --git a/packages/sw-plugin/src/index.js b/packages/sw-plugin/src/index.js index 4e065556b..60acb722a 100644 --- a/packages/sw-plugin/src/index.js +++ b/packages/sw-plugin/src/index.js @@ -1,7 +1,6 @@ import path from 'path'; import { request } from 'http'; - -const pathToPosix = p => p.split(path.sep).join(path.posix.sep); +import { normalizePath } from 'wmr'; /** * Service Worker plugin for WMR. @@ -19,7 +18,7 @@ export default function swPlugin(options) { const wmrProxyPlugin = { resolveId(id) { - const normalizedId = id[1] + id[2] === ':\\' ? pathToPosix(id.slice(2)) : id; + const normalizedId = id[1] + id[2] === ':\\' ? normalizePath(id.slice(2)) : id; if (id.startsWith('/@npm/')) return id; if (!/^\.*\//.test(normalizedId)) return '/@npm/' + id; }, @@ -49,7 +48,7 @@ export default function swPlugin(options) { async resolveId(id, importer) { if (!id.startsWith('sw:')) return; const resolved = await this.resolve(id.slice(3), importer); - if (resolved) return `\0sw:${pathToPosix(resolved.id)}`; + if (resolved) return `\0sw:${normalizePath(resolved.id)}`; }, async load(id) { if (!id.startsWith('\0sw:')) return; diff --git a/packages/wmr/index.js b/packages/wmr/index.js index 738d2e0c4..47b0ad92f 100644 --- a/packages/wmr/index.js +++ b/packages/wmr/index.js @@ -1,5 +1,14 @@ +import path from 'path'; + /** * Wrapper for improved intellisense completion * @type {typeof import("wmr").defineConfig} */ export const defineConfig = config => config; + +/** + * Normalize a file path across OSes. + * @param {string} file + * @returns {string} + */ +export const normalizePath = file => file.split(path.win32.sep).join(path.posix.sep); diff --git a/packages/wmr/src/bundler.js b/packages/wmr/src/bundler.js index 29657284a..1071253ee 100644 --- a/packages/wmr/src/bundler.js +++ b/packages/wmr/src/bundler.js @@ -1,11 +1,9 @@ -import { relative, sep, posix, resolve, dirname } from 'path'; +import { relative, posix, resolve, dirname } from 'path'; import * as rollup from 'rollup'; import terser from './plugins/fast-minify.js'; import totalist from 'totalist'; import { getPlugins } from './lib/plugins.js'; - -/** @param {string} p */ -const pathToPosix = p => p.split(sep).join(posix.sep); +import { normalizePath } from '../index.js'; /** @param {import('wmr').BuildOptions} options */ export async function bundleProd(options) { @@ -18,7 +16,7 @@ export async function bundleProd(options) { await totalist(cwd, (rel, abs) => { if (ignore.test(abs)) return; if (!/\.html?/.test(rel)) return; - input.push('./' + pathToPosix(relative(root, abs))); + input.push('./' + normalizePath(relative(root, abs))); }); const bundle = await rollup.rollup({ @@ -46,7 +44,7 @@ export async function bundleProd(options) { plugins: [minify && terser({ compress: true, sourcemap })], sourcemap, sourcemapPathTransform(p, mapPath) { - let url = pathToPosix(relative(cwd, resolve(dirname(mapPath), p))); + let url = normalizePath(relative(cwd, resolve(dirname(mapPath), p))); // strip leading relative path url = url.replace(/^\.\//g, ''); // replace internal npm prefix diff --git a/packages/wmr/src/lib/rollup-plugin-container.js b/packages/wmr/src/lib/rollup-plugin-container.js index 4d0cc9823..d2203a9f1 100644 --- a/packages/wmr/src/lib/rollup-plugin-container.js +++ b/packages/wmr/src/lib/rollup-plugin-container.js @@ -1,18 +1,17 @@ -import { resolve, relative, dirname, sep, posix } from 'path'; +import { resolve, relative, dirname, posix } from 'path'; import { createHash } from 'crypto'; import { promises as fs } from 'fs'; import * as acorn from 'acorn'; import * as kl from 'kolorist'; import acornClassFields from 'acorn-class-fields'; import { debug, formatResolved, formatPath } from './output-utils.js'; +import { normalizePath } from '../../index.js'; // Rollup respects "module", Node 14 doesn't. const cjsDefault = m => ('default' in m ? m.default : m); /** @type acorn */ const { Parser } = cjsDefault(acorn); -const toPosixPath = path => path.split(sep).join(posix.sep); - /** Fast splice(x,1) when order doesn't matter (h/t Rich) * @param {Array} array @param {number} index */ @@ -54,7 +53,7 @@ export function createPluginContainer(plugins, opts = {}) { const MODULES = opts.modules || new Map(); function generateFilename({ type, name, fileName, source }) { - const posixName = toPosixPath(name); + const posixName = normalizePath(name); if (!fileName) { fileName = (type === 'entry' && ctx.outputOptions.file) || ctx.outputOptions[type + 'FileNames'] || '[name][extname]'; @@ -321,7 +320,7 @@ export function createPluginContainer(plugins, opts = {}) { return result; } } - return JSON.stringify('/' + fileName.split(sep).join(posix.sep)); + return JSON.stringify('/' + normalizePath(fileName)); } }; diff --git a/packages/wmr/src/plugins/url-plugin.js b/packages/wmr/src/plugins/url-plugin.js index c700fa3e2..ec8cbbaff 100644 --- a/packages/wmr/src/plugins/url-plugin.js +++ b/packages/wmr/src/plugins/url-plugin.js @@ -1,5 +1,6 @@ -import { relative, basename, sep, posix } from 'path'; +import { relative, basename } from 'path'; import { promises as fs } from 'fs'; +import { normalizePath } from '../../index.js'; export const IMPLICIT_URL = /\.(?:png|jpe?g|gif|webp|svg|mp4|webm|ogg|mp3|wav|flac|aac|woff2?|eot|ttf|otf)$/i; @@ -27,7 +28,7 @@ export default function urlPlugin({ inline, cwd } = {}) { // In dev mode, we turn the import into an inline module that avoids a network request: if (inline) { - const url = '/' + relative(cwd, resolved.id).replace(/^\./, '').split(sep).join(posix.sep) + '?asset'; + const url = '/' + normalizePath(relative(cwd, resolved.id).replace(/^\./, '')) + '?asset'; return { id: escapeUrl(`data:text/javascript,export default${JSON.stringify(url)}`), external: true diff --git a/packages/wmr/src/plugins/wmr/styles-plugin.js b/packages/wmr/src/plugins/wmr/styles-plugin.js index cac5fa403..d3a0b8adf 100644 --- a/packages/wmr/src/plugins/wmr/styles-plugin.js +++ b/packages/wmr/src/plugins/wmr/styles-plugin.js @@ -1,5 +1,6 @@ import { promises as fs } from 'fs'; import { basename, dirname, relative, resolve, sep, posix } from 'path'; +import { normalizePath } from '../../../index.js'; import { transformCssImports } from '../../lib/transform-css-imports.js'; import { transformCss } from '../../lib/transform-css.js'; @@ -20,7 +21,7 @@ export function processSass(sass) { */ export async function modularizeCss(css, id, mappings = [], idAbsolute) { // normalize to posix id for consistent hashing - if (id.match(/^[^/]*\\/)) id = id.split(sep).join(posix.sep); + if (id.match(/^[^/]*\\/)) id = normalizePath(id); const suffix = '_' + hash(id); @@ -123,7 +124,7 @@ export default function wmrStylesPlugin({ cwd, hot, fullPath, production } = {}) const isModular = /\.module\.(css|s[ac]ss)$/.test(id); let idRelative = cwd ? relative(cwd || '', resolve(cwd, id)) : multiRelative(cwds, id); - if (idRelative.match(/^[^/]*\\/)) idRelative = idRelative.split(sep).join(posix.sep); + if (idRelative.match(/^[^/]*\\/)) idRelative = normalizePath(idRelative); const mappings = []; if (isModular) { diff --git a/packages/wmr/src/wmr-middleware.js b/packages/wmr/src/wmr-middleware.js index 5664908f0..94eb3659e 100644 --- a/packages/wmr/src/wmr-middleware.js +++ b/packages/wmr/src/wmr-middleware.js @@ -11,6 +11,7 @@ import { getMimeType } from './lib/mimetypes.js'; import { debug, formatPath } from './lib/output-utils.js'; import { getPlugins } from './lib/plugins.js'; import { watch } from './lib/fs-watcher.js'; +import { normalizePath } from '../index.js'; const NOOP = () => {}; @@ -96,7 +97,7 @@ export default function wmrMiddleware(options) { watcher.on('change', filename => { NonRollup.watchChange(resolve(cwd, filename)); // normalize paths to 'nix: - filename = filename.split(sep).join(posix.sep); + filename = normalizePath(filename); // Delete any generated CSS Modules mapping modules: const suffix = /\.module\.(css|s[ac]ss)$/.test(filename) ? '.js' : ''; @@ -158,7 +159,7 @@ export default function wmrMiddleware(options) { let file = resolve(cwd, osPath); // Rollup-style CWD-relative Unix-normalized path "id": - let id = relative(cwd, file).replace(/^\.\//, '').replace(/^[\0]/, '').split(sep).join(posix.sep); + let id = normalizePath(relative(cwd, file).replace(/^\.\//, '').replace(/^[\0]/, '')); // add back any prefix if there was one: file = prefix + file; @@ -319,7 +320,7 @@ export const TRANSFORMS = { if (resolved) { spec = typeof resolved == 'object' ? resolved.id : resolved; if (/^(\/|\\|[a-z]:\\)/i.test(spec)) { - spec = relative(dirname(file), spec).split(sep).join(posix.sep); + spec = normalizePath(relative(dirname(file), spec)); if (!/^\.?\.?\//.test(spec)) { spec = './' + spec; } @@ -330,7 +331,7 @@ export const TRANSFORMS = { return spec; } - spec = relative(cwd, spec).split(sep).join(posix.sep); + spec = normalizePath(relative(cwd, spec)); if (!/^(\/|[\w-]+:)/.test(spec)) spec = `/${spec}`; return spec; } @@ -340,7 +341,7 @@ export const TRANSFORMS = { spec = spec.replace(/^\0?([a-z-]+):(.+)$/, (s, prefix, spec) => { // \0abc:/abs/disk/path --> /@abc/cwd-relative-path if (spec[0] === '/' || spec[0] === sep) { - spec = relative(cwd, spec).split(sep).join(posix.sep); + spec = normalizePath(relative(cwd, spec)); } return '/@' + prefix + '/' + spec; }); diff --git a/packages/wmr/types.d.ts b/packages/wmr/types.d.ts index 2ec6d1309..3abffb62f 100644 --- a/packages/wmr/types.d.ts +++ b/packages/wmr/types.d.ts @@ -68,6 +68,8 @@ declare module 'wmr' { export function defineConfig< T extends Partial | ((options: Options) => void | Partial | Promise>) >(options: T): T; + + export function normalizePath(path: string): string; } // Declarations used by WMR-based applications