From 81d1841bcba81b0b8d75f656558ead18cfc53042 Mon Sep 17 00:00:00 2001 From: Alice Pote Date: Wed, 5 Jul 2023 09:20:42 -0500 Subject: [PATCH] TEST should fail on windows In #4317 as part of removing in-browser compilation support we removed the polyfills for nodejs built-in modules like `path` which were injected by Rollup during build-time. Although the _main_ purpose of these polyfills was allowing Stencil to run in the browser in the case of the `path` module there was also a secondary purpose which was ensuring that paths were treated the same way across supported platforms (posix + windows). See, for instance, the following lines in the polyfill: https://github.com/ionic-team/stencil/blob/b911f1986a0d583bd1e3cd42cbbca9b255c32f2d/src/compiler/sys/modules/path.ts#L35-L38 These functions basically wrapped the existing path implementation with our `normalizePath` helper, which would ensure that the output paths would be the same on both windows and posix systems (e.g. macOS and linux). When we merged #4317 an effort was made to add `normalizePath` around the codebase where it was thought that various paths being calculated needed to be platform-independent, however, a few locations were missed (in particular, some paths output into typedefs, which would show up as non-posix paths when building on windows). To address the issue we introduce `relative` and `join` functions into the existing path-related `utils` file (which is incidentally renamed) which work similarly to how the patched functions in the old polyfill did. Then several usage sites are changed to import those new utils instead of the 'raw' functions from `path`. Together these changes should ensure that Stencil's output is not platform-dependent. See here for an issue reporting the problem: #4543 --- src/compiler/build/build-finish.ts | 3 +- .../output-targets/dist-collection/index.ts | 7 +++-- .../custom-elements-types.ts | 6 ++-- .../output-targets/output-lazy-loader.ts | 11 ++++--- src/compiler/output-targets/output-www.ts | 3 +- .../test/custom-elements-types.spec.ts | 23 +++++--------- .../test/output-targets-collection.spec.ts | 3 +- src/compiler/prerender/prerender-queue.ts | 3 +- .../service-worker/service-worker-util.ts | 5 ++-- .../collections/parse-collection-module.ts | 4 +-- .../map-imports-to-path-aliases.ts | 4 +-- .../transformers/rewrite-aliased-paths.ts | 4 +-- .../transformers/static-to-meta/component.ts | 4 +-- .../transformers/stencil-import-path.ts | 5 ++-- src/compiler/transformers/type-library.ts | 6 ++-- src/compiler/transpile/run-program.ts | 11 +++++-- src/compiler/transpile/validate-components.ts | 3 +- src/utils/index.ts | 2 +- src/utils/logger/logger-typescript.ts | 2 +- src/utils/{normalize-path.ts => path.ts} | 30 +++++++++++++++++++ src/utils/test/normalize-path.spec.ts | 2 +- 21 files changed, 81 insertions(+), 60 deletions(-) rename src/utils/{normalize-path.ts => path.ts} (86%) diff --git a/src/compiler/build/build-finish.ts b/src/compiler/build/build-finish.ts index 91c0016b5cf2..b52919f782ea 100644 --- a/src/compiler/build/build-finish.ts +++ b/src/compiler/build/build-finish.ts @@ -1,5 +1,4 @@ -import { isFunction, isRemoteUrl } from '@utils'; -import { relative } from 'path'; +import { isFunction, isRemoteUrl, relative } from '@utils'; import type * as d from '../../declarations'; import { IS_NODE_ENV } from '../sys/environment'; diff --git a/src/compiler/output-targets/dist-collection/index.ts b/src/compiler/output-targets/dist-collection/index.ts index 5433299e468d..4ad54a9d0a6e 100644 --- a/src/compiler/output-targets/dist-collection/index.ts +++ b/src/compiler/output-targets/dist-collection/index.ts @@ -4,10 +4,11 @@ import { flatOne, generatePreamble, isOutputTargetDistCollection, + join, normalizePath, + relative, sortBy, } from '@utils'; -import { join, relative } from 'path'; import ts from 'typescript'; import type * as d from '../../../declarations'; @@ -108,10 +109,10 @@ const writeCollectionManifest = async ( outputTarget: d.OutputTargetDistCollection ) => { // get the absolute path to the directory where the collection will be saved - const collectionDir = normalizePath(outputTarget.collectionDir); + const { collectionDir } = outputTarget; // create an absolute file path to the actual collection json file - const collectionFilePath = normalizePath(join(collectionDir, COLLECTION_MANIFEST_FILE_NAME)); + const collectionFilePath = join(collectionDir, COLLECTION_MANIFEST_FILE_NAME); // don't bother serializing/writing the collection if we're not creating a distribution await compilerCtx.fs.writeFile(collectionFilePath, collectionData); diff --git a/src/compiler/output-targets/dist-custom-elements/custom-elements-types.ts b/src/compiler/output-targets/dist-custom-elements/custom-elements-types.ts index fbee63127e94..886a84e16e9e 100644 --- a/src/compiler/output-targets/dist-custom-elements/custom-elements-types.ts +++ b/src/compiler/output-targets/dist-custom-elements/custom-elements-types.ts @@ -1,5 +1,5 @@ -import { dashToPascalCase, isOutputTargetDistCustomElements, normalizePath } from '@utils'; -import { dirname, join, relative } from 'path'; +import { dashToPascalCase, isOutputTargetDistCustomElements, join, normalizePath, relative } from '@utils'; +import { dirname } from 'path'; import type * as d from '../../../declarations'; @@ -46,7 +46,7 @@ const generateCustomElementsTypesOutput = async ( const isBundleExport = outputTarget.customElementsExportBehavior === 'bundle'; // the path where we're going to write the typedef for the whole dist-custom-elements output - const customElementsDtsPath = join(outputTarget.dir!, 'index.d.ts'); + const customElementsDtsPath = normalizePath(join(outputTarget.dir!, 'index.d.ts')); // the directory where types for the individual components are written const componentsTypeDirectoryRelPath = relative(outputTarget.dir!, typesDir); diff --git a/src/compiler/output-targets/output-lazy-loader.ts b/src/compiler/output-targets/output-lazy-loader.ts index dc066335d0b9..409293097e5c 100644 --- a/src/compiler/output-targets/output-lazy-loader.ts +++ b/src/compiler/output-targets/output-lazy-loader.ts @@ -1,5 +1,4 @@ -import { generatePreamble, isOutputTargetDistLazyLoader, normalizePath, relativeImport } from '@utils'; -import { join, relative } from 'path'; +import { generatePreamble, isOutputTargetDistLazyLoader, join, relative, relativeImport } from '@utils'; import type * as d from '../../declarations'; import { getClientPolyfill } from '../app-core/app-polyfills'; @@ -49,18 +48,18 @@ const generateLoader = async ( const es2017EntryPoint = join(es2017Dir, 'loader.js'); const polyfillsEntryPoint = join(es2017Dir, 'polyfills/index.js'); const cjsEntryPoint = join(cjsDir, 'loader.cjs.js'); - const polyfillsExport = `export * from '${normalizePath(relative(loaderPath, polyfillsEntryPoint))}';`; + const polyfillsExport = `export * from '${relative(loaderPath, polyfillsEntryPoint)}';`; const indexContent = `${generatePreamble(config)} ${es5HtmlElement} ${polyfillsExport} -export * from '${normalizePath(relative(loaderPath, es5EntryPoint))}'; +export * from '${relative(loaderPath, es5EntryPoint)}'; `; const indexES2017Content = `${generatePreamble(config)} ${polyfillsExport} -export * from '${normalizePath(relative(loaderPath, es2017EntryPoint))}'; +export * from '${relative(loaderPath, es2017EntryPoint)}'; `; const indexCjsContent = `${generatePreamble(config)} -module.exports = require('${normalizePath(relative(loaderPath, cjsEntryPoint))}'); +module.exports = require('${relative(loaderPath, cjsEntryPoint)}'); module.exports.applyPolyfills = function() { return Promise.resolve() }; `; diff --git a/src/compiler/output-targets/output-www.ts b/src/compiler/output-targets/output-www.ts index 0ee9fd6527bf..c8885386e689 100644 --- a/src/compiler/output-targets/output-www.ts +++ b/src/compiler/output-targets/output-www.ts @@ -1,6 +1,5 @@ import { cloneDocument, serializeNodeToHtml } from '@stencil/core/mock-doc'; -import { catchError, flatOne, isOutputTargetWww, unique } from '@utils'; -import { join, relative } from 'path'; +import { catchError, flatOne, isOutputTargetWww, join, relative, unique } from '@utils'; import type * as d from '../../declarations'; import { generateEs5DisabledMessage } from '../app-core/app-es5-disabled'; diff --git a/src/compiler/output-targets/test/custom-elements-types.spec.ts b/src/compiler/output-targets/test/custom-elements-types.spec.ts index 5529672d1ba9..8005296586d6 100644 --- a/src/compiler/output-targets/test/custom-elements-types.spec.ts +++ b/src/compiler/output-targets/test/custom-elements-types.spec.ts @@ -5,9 +5,8 @@ import { mockModule, mockValidatedConfig, } from '@stencil/core/testing'; -import { DIST_CUSTOM_ELEMENTS } from '@utils'; +import { DIST_CUSTOM_ELEMENTS, normalizePath } from '@utils'; import path from 'path'; -import { join, relative } from 'path'; import type * as d from '../../../declarations'; import { stubComponentCompilerMeta } from '../../types/tests/ComponentCompilerMeta.stub'; @@ -29,8 +28,8 @@ const setup = () => { const buildCtx = mockBuildCtx(config, compilerCtx); const root = config.rootDir; - config.rootDir = path.join(root, 'User', 'testing', '/'); - config.globalScript = path.join(root, 'User', 'testing', 'src', 'global.ts'); + config.rootDir = normalizePath(path.join(root, 'User', 'testing', '/')); + config.globalScript = normalizePath(path.join(root, 'User', 'testing', 'src', 'global.ts')); const bundleCustomElementsSpy = jest.spyOn(outputCustomElementsMod, 'bundleCustomElements'); @@ -62,17 +61,11 @@ describe('Custom Elements Typedef generation', () => { await generateCustomElementsTypes(config, compilerCtx, buildCtx, 'types_dir'); - const componentsTypeDirectoryPath = relative('my-best-dir', join('types_dir', 'components')); - const expectedTypedefOutput = [ '/* TestApp custom elements */', - `export { StubCmp as MyComponent } from '${join(componentsTypeDirectoryPath, 'my-component', 'my-component')}';`, + `export { StubCmp as MyComponent } from '../types_dir/components/my-component/my-component';`, `export { defineCustomElement as defineCustomElementMyComponent } from './my-component';`, - `export { MyBestComponent as MyBestComponent } from '${join( - componentsTypeDirectoryPath, - 'the-other-component', - 'my-real-best-component' - )}';`, + `export { MyBestComponent as MyBestComponent } from '../types_dir/components/the-other-component/my-real-best-component';`, `export { defineCustomElement as defineCustomElementMyBestComponent } from './my-best-component';`, '', '/**', @@ -106,7 +99,7 @@ describe('Custom Elements Typedef generation', () => { '', ].join('\n'); - expect(compilerCtx.fs.writeFile).toHaveBeenCalledWith(join('my-best-dir', 'index.d.ts'), expectedTypedefOutput, { + expect(compilerCtx.fs.writeFile).toHaveBeenCalledWith('./my-best-dir/index.d.ts', expectedTypedefOutput, { outputTargetType: DIST_CUSTOM_ELEMENTS, }); @@ -165,7 +158,7 @@ describe('Custom Elements Typedef generation', () => { '', ].join('\n'); - expect(compilerCtx.fs.writeFile).toHaveBeenCalledWith(join('my-best-dir', 'index.d.ts'), expectedTypedefOutput, { + expect(compilerCtx.fs.writeFile).toHaveBeenCalledWith('./my-best-dir/index.d.ts', expectedTypedefOutput, { outputTargetType: DIST_CUSTOM_ELEMENTS, }); @@ -233,7 +226,7 @@ describe('Custom Elements Typedef generation', () => { '', ].join('\n'); - expect(compilerCtx.fs.writeFile).toHaveBeenCalledWith(join('my-best-dir', 'index.d.ts'), expectedTypedefOutput, { + expect(compilerCtx.fs.writeFile).toHaveBeenCalledWith('./my-best-dir/index.d.ts', expectedTypedefOutput, { outputTargetType: DIST_CUSTOM_ELEMENTS, }); diff --git a/src/compiler/output-targets/test/output-targets-collection.spec.ts b/src/compiler/output-targets/test/output-targets-collection.spec.ts index f88b1c6c4f0c..da1185d23fc1 100644 --- a/src/compiler/output-targets/test/output-targets-collection.spec.ts +++ b/src/compiler/output-targets/test/output-targets-collection.spec.ts @@ -1,5 +1,4 @@ import { mockBuildCtx, mockCompilerCtx, mockModule, mockValidatedConfig } from '@stencil/core/testing'; -import { normalize } from 'path'; import type * as d from '../../../declarations'; import * as test from '../../transformers/map-imports-to-path-aliases'; @@ -61,7 +60,7 @@ describe('Dist Collection output target', () => { await outputCollection(mockConfig, mockedCompilerCtx, mockedBuildCtx, changedModules); - expect(mapImportPathSpy).toHaveBeenCalledWith(mockConfig, normalize('/dist/collection/main.js'), { + expect(mapImportPathSpy).toHaveBeenCalledWith(mockConfig, '/dist/collection/main.js', { collectionDir: '/dist/collection', dir: '', transformAliasedImportPaths, diff --git a/src/compiler/prerender/prerender-queue.ts b/src/compiler/prerender/prerender-queue.ts index 3d6bb0533633..91bc0fc0c982 100644 --- a/src/compiler/prerender/prerender-queue.ts +++ b/src/compiler/prerender/prerender-queue.ts @@ -1,5 +1,4 @@ -import { buildError, catchError, isFunction, isString } from '@utils'; -import { relative } from 'path'; +import { buildError, catchError, isFunction, isString, relative } from '@utils'; import type * as d from '../../declarations'; import { crawlAnchorsForNextUrls } from './crawl-urls'; diff --git a/src/compiler/service-worker/service-worker-util.ts b/src/compiler/service-worker/service-worker-util.ts index 44a72fc8ddff..5bfdfa1ff7d9 100644 --- a/src/compiler/service-worker/service-worker-util.ts +++ b/src/compiler/service-worker/service-worker-util.ts @@ -1,10 +1,9 @@ -import { normalizePath } from '@utils'; -import { relative } from 'path'; +import { relative } from '@utils'; import type * as d from '../../declarations'; export const generateServiceWorkerUrl = (outputTarget: d.OutputTargetWww, serviceWorker: d.ServiceWorkerConfig) => { - let swUrl = normalizePath(relative(outputTarget.appDir, serviceWorker.swDest)); + let swUrl = relative(outputTarget.appDir, serviceWorker.swDest); if (swUrl.charAt(0) !== '/') { swUrl = '/' + swUrl; diff --git a/src/compiler/transformers/collections/parse-collection-module.ts b/src/compiler/transformers/collections/parse-collection-module.ts index 040db22e26e0..6196396a3cf3 100644 --- a/src/compiler/transformers/collections/parse-collection-module.ts +++ b/src/compiler/transformers/collections/parse-collection-module.ts @@ -1,5 +1,5 @@ -import { normalizePath } from '@utils'; -import { dirname, join, relative } from 'path'; +import { join, normalizePath, relative } from '@utils'; +import { dirname } from 'path'; import type * as d from '../../../declarations'; import { parseCollectionManifest } from './parse-collection-manifest'; diff --git a/src/compiler/transformers/map-imports-to-path-aliases.ts b/src/compiler/transformers/map-imports-to-path-aliases.ts index b0194b5fe5a7..c3910a0703fd 100644 --- a/src/compiler/transformers/map-imports-to-path-aliases.ts +++ b/src/compiler/transformers/map-imports-to-path-aliases.ts @@ -1,5 +1,5 @@ -import { normalizePath } from '@utils'; -import { dirname, relative } from 'path'; +import { normalizePath, relative } from '@utils'; +import { dirname } from 'path'; import ts from 'typescript'; import type * as d from '../../declarations'; diff --git a/src/compiler/transformers/rewrite-aliased-paths.ts b/src/compiler/transformers/rewrite-aliased-paths.ts index 36b406d9cb9f..42c1e6e7d565 100644 --- a/src/compiler/transformers/rewrite-aliased-paths.ts +++ b/src/compiler/transformers/rewrite-aliased-paths.ts @@ -1,5 +1,5 @@ -import { normalizePath } from '@utils'; -import { dirname, relative } from 'path'; +import { normalizePath, relative } from '@utils'; +import { dirname } from 'path'; import ts from 'typescript'; import { retrieveTsModifiers } from './transform-utils'; diff --git a/src/compiler/transformers/static-to-meta/component.ts b/src/compiler/transformers/static-to-meta/component.ts index 447c226a3527..284a2f8e17c7 100644 --- a/src/compiler/transformers/static-to-meta/component.ts +++ b/src/compiler/transformers/static-to-meta/component.ts @@ -1,5 +1,5 @@ -import { normalizePath, unique } from '@utils'; -import { dirname, isAbsolute, join, relative } from 'path'; +import { join, normalizePath, relative, unique } from '@utils'; +import { dirname, isAbsolute } from 'path'; import ts from 'typescript'; import type * as d from '../../../declarations'; diff --git a/src/compiler/transformers/stencil-import-path.ts b/src/compiler/transformers/stencil-import-path.ts index 9323999cece8..7430e9728d1c 100644 --- a/src/compiler/transformers/stencil-import-path.ts +++ b/src/compiler/transformers/stencil-import-path.ts @@ -1,5 +1,5 @@ -import { DEFAULT_STYLE_MODE, isString, normalizePath } from '@utils'; -import { basename, dirname, isAbsolute, relative } from 'path'; +import { DEFAULT_STYLE_MODE, isString, relative } from '@utils'; +import { basename, dirname, isAbsolute } from 'path'; import type { ImportData, ParsedImport, SerializeImportData } from '../../declarations'; @@ -24,7 +24,6 @@ export const serializeImportPath = (data: SerializeImportData, styleImportData: if (isString(data.importerPath) && isAbsolute(data.importeePath)) { p = relative(dirname(data.importerPath), data.importeePath); } - p = normalizePath(p); if (!p.startsWith('.')) { p = './' + p; } diff --git a/src/compiler/transformers/type-library.ts b/src/compiler/transformers/type-library.ts index 2e82adcdf375..1472bbfb4087 100644 --- a/src/compiler/transformers/type-library.ts +++ b/src/compiler/transformers/type-library.ts @@ -1,5 +1,4 @@ -import { normalizePath } from '@utils'; -import path from 'path'; +import { normalizePath, relative } from '@utils'; import ts from 'typescript'; import type * as d from '../../declarations'; @@ -30,8 +29,7 @@ export function addToLibrary( checker: ts.TypeChecker, pathToTypeModule: string ): string { - pathToTypeModule = path.relative(process.cwd(), pathToTypeModule); - pathToTypeModule = normalizePath(pathToTypeModule, false); + pathToTypeModule = relative(process.cwd(), pathToTypeModule); // for now we don't make any attempt to include types in node_modules if (pathToTypeModule.startsWith('node_modules')) { diff --git a/src/compiler/transpile/run-program.ts b/src/compiler/transpile/run-program.ts index 0e11f00c39e0..b092d0811ff0 100644 --- a/src/compiler/transpile/run-program.ts +++ b/src/compiler/transpile/run-program.ts @@ -1,5 +1,12 @@ -import { getComponentsFromModules, isOutputTargetDistTypes, loadTypeScriptDiagnostics, normalizePath } from '@utils'; -import { basename, join, relative } from 'path'; +import { + getComponentsFromModules, + isOutputTargetDistTypes, + join, + loadTypeScriptDiagnostics, + normalizePath, + relative, +} from '@utils'; +import { basename } from 'path'; import ts from 'typescript'; import type * as d from '../../declarations'; diff --git a/src/compiler/transpile/validate-components.ts b/src/compiler/transpile/validate-components.ts index 2dbf304509c2..f52dc5f02185 100644 --- a/src/compiler/transpile/validate-components.ts +++ b/src/compiler/transpile/validate-components.ts @@ -1,5 +1,4 @@ -import { buildError } from '@utils'; -import { relative } from 'path'; +import { buildError, relative } from '@utils'; import type * as d from '../../declarations'; diff --git a/src/utils/index.ts b/src/utils/index.ts index b345a5cb5fcc..e7c2ace7ed87 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -8,8 +8,8 @@ export * from './logger/logger-rollup'; export * from './logger/logger-typescript'; export * from './logger/logger-utils'; export * from './message-utils'; -export * from './normalize-path'; export * from './output-target'; +export * from './path'; export * from './query-nonce-meta-tag-content'; export * from './sourcemaps'; export * from './url-paths'; diff --git a/src/utils/logger/logger-typescript.ts b/src/utils/logger/logger-typescript.ts index e55e4a61a0f9..4b4761b31d1c 100644 --- a/src/utils/logger/logger-typescript.ts +++ b/src/utils/logger/logger-typescript.ts @@ -2,7 +2,7 @@ import type { Diagnostic, DiagnosticMessageChain, Node } from 'typescript'; import type * as d from '../../declarations'; import { isIterable } from '../helpers'; -import { normalizePath } from '../normalize-path'; +import { normalizePath } from '../path'; import { splitLineBreaks } from './logger-utils'; /** diff --git a/src/utils/normalize-path.ts b/src/utils/path.ts similarity index 86% rename from src/utils/normalize-path.ts rename to src/utils/path.ts index 40cdbbf9a01a..4e8da8e2aeb4 100644 --- a/src/utils/normalize-path.ts +++ b/src/utils/path.ts @@ -1,3 +1,5 @@ +import path from 'path'; + /** * Convert Windows backslash paths to slash paths: foo\\bar ➔ foo/bar * Forward-slash paths can be used in Windows as long as they're not @@ -187,3 +189,31 @@ const enum CharacterCodes { percent = 0x25, // % slash = 0x2f, // / } + +/** + * A wrapped version of node.js' {@link path.relative} which adds our custom + * normalization logic. This solves the relative path between `from` and `to`! + * + * @throws the underlying node.js function can throw if either path is not a + * string + * @param from the path where relative resolution starts + * @param to the destination path + * @returns the resolved relative path + */ +export function relative(from: string, to: string): string { + return normalizePath(path.relative(from, to), false); +} + +/** + * A wrapped version of node.js' {@link path.join} which adds our custom + * normalization logic. This joins all the arguments (path fragments) into a + * single path. + * + * @throws the underlying node function will throw if any argument is not a + * string + * @param paths the paths to join together + * @returns a joined path! + */ +export function join(...paths: string[]): string { + return normalizePath(path.join(...paths), false); +} diff --git a/src/utils/test/normalize-path.spec.ts b/src/utils/test/normalize-path.spec.ts index 13beca381ba6..cc72fea4edf4 100644 --- a/src/utils/test/normalize-path.spec.ts +++ b/src/utils/test/normalize-path.spec.ts @@ -1,4 +1,4 @@ -import { normalizeFsPathQuery, normalizePath } from '../normalize-path'; +import { normalizeFsPathQuery, normalizePath } from '../path'; describe('normalizePath', () => { it('node module', () => {