diff --git a/src/compiler/config/outputs/validate-collection.ts b/src/compiler/config/outputs/validate-collection.ts index f072bed7e86..6b92d4e34cc 100644 --- a/src/compiler/config/outputs/validate-collection.ts +++ b/src/compiler/config/outputs/validate-collection.ts @@ -17,7 +17,8 @@ export const validateCollection = ( return userOutputs.filter(isOutputTargetDistCollection).map((outputTarget) => { return { ...outputTarget, - dir: getAbsolutePath(config, outputTarget.dir || 'dist/collection'), + transformAliasedImportPaths: outputTarget.transformAliasedImportPaths ?? false, + dir: getAbsolutePath(config, outputTarget.dir ?? 'dist/collection'), }; }); }; diff --git a/src/compiler/config/outputs/validate-dist.ts b/src/compiler/config/outputs/validate-dist.ts index 480bf807d06..2fc3853611c 100644 --- a/src/compiler/config/outputs/validate-dist.ts +++ b/src/compiler/config/outputs/validate-dist.ts @@ -67,6 +67,7 @@ export const validateDist = (config: d.ValidatedConfig, userOutputs: d.OutputTar dir: distOutputTarget.dir, collectionDir: distOutputTarget.collectionDir, empty: distOutputTarget.empty, + transformAliasedImportPaths: distOutputTarget.transformAliasedImportPathsInCollection, }); outputs.push({ type: COPY, @@ -134,6 +135,7 @@ const validateOutputTargetDist = (config: d.ValidatedConfig, o: d.OutputTargetDi copy: validateCopy(o.copy ?? [], []), polyfills: isBoolean(o.polyfills) ? o.polyfills : undefined, empty: isBoolean(o.empty) ? o.empty : true, + transformAliasedImportPathsInCollection: o.transformAliasedImportPathsInCollection ?? false, }; if (!isAbsolute(outputTarget.buildDir)) { diff --git a/src/compiler/config/test/validate-output-dist-collection.spec.ts b/src/compiler/config/test/validate-output-dist-collection.spec.ts new file mode 100644 index 00000000000..f87a23b4cf4 --- /dev/null +++ b/src/compiler/config/test/validate-output-dist-collection.spec.ts @@ -0,0 +1,87 @@ +import type * as d from '@stencil/core/declarations'; +import { validateConfig } from '../validate-config'; +import { mockConfig, mockLoadConfigInit } from '@stencil/core/testing'; +import { resolve, join } from 'path'; + +describe('validateDistCollectionOutputTarget', () => { + let config: d.Config; + + const rootDir = resolve('/'); + const defaultDir = join(rootDir, 'dist', 'collection'); + + beforeEach(() => { + config = mockConfig(); + }); + + it('sets correct default values', () => { + const target: d.OutputTargetDistCollection = { + type: 'dist-collection', + empty: false, + dir: null, + collectionDir: null, + }; + config.outputTargets = [target]; + + const { config: validatedConfig } = validateConfig(config, mockLoadConfigInit()); + + expect(validatedConfig.outputTargets).toEqual([ + { + type: 'dist-collection', + empty: false, + dir: defaultDir, + collectionDir: null, + transformAliasedImportPaths: false, + }, + ]); + }); + + it('sets specified directory', () => { + const target: d.OutputTargetDistCollection = { + type: 'dist-collection', + empty: false, + dir: '/my-dist', + collectionDir: null, + }; + config.outputTargets = [target]; + + const { config: validatedConfig } = validateConfig(config, mockLoadConfigInit()); + + expect(validatedConfig.outputTargets).toEqual([ + { + type: 'dist-collection', + empty: false, + dir: '/my-dist', + collectionDir: null, + transformAliasedImportPaths: false, + }, + ]); + }); + + describe('transformAliasedImportPaths', () => { + it.each([false, true])( + "sets option '%s' when explicitly '%s' in config", + (transformAliasedImportPaths: boolean) => { + const target: d.OutputTargetDistCollection = { + type: 'dist-collection', + empty: false, + dir: null, + collectionDir: null, + transformAliasedImportPaths, + }; + config.outputTargets = [target]; + + const { config: validatedConfig } = validateConfig(config, mockLoadConfigInit()); + + expect(validatedConfig.outputTargets).toEqual([ + { + type: 'dist-collection', + empty: false, + dir: defaultDir, + collectionDir: null, + transformAliasedImportPaths, + }, + ]); + } + ); + }); +}); diff --git a/src/compiler/config/test/validate-output-dist.spec.ts b/src/compiler/config/test/validate-output-dist.spec.ts index b9951dca25f..b5a8417caec 100644 --- a/src/compiler/config/test/validate-output-dist.spec.ts +++ b/src/compiler/config/test/validate-output-dist.spec.ts @@ -32,6 +32,7 @@ describe('validateDistOutputTarget', () => { type: 'dist', polyfills: undefined, typesDir: path.join(rootDir, 'my-dist', 'types'), + transformAliasedImportPathsInCollection: false, }, { esmDir: path.join(rootDir, 'my-dist', 'my-build', 'testing'), @@ -62,6 +63,7 @@ describe('validateDistOutputTarget', () => { collectionDir: path.join(rootDir, 'my-dist', 'collection'), dir: path.join(rootDir, '/my-dist'), empty: false, + transformAliasedImportPaths: false, type: 'dist-collection', }, { @@ -107,4 +109,90 @@ describe('validateDistOutputTarget', () => { const { config } = validateConfig(userConfig, mockLoadConfigInit()); expect(config.outputTargets.some((o) => o.type === 'dist')).toBe(false); }); + + it('sets option to transform aliased import paths when enabled', () => { + const outputTarget: d.OutputTargetDist = { + type: 'dist', + dir: 'my-dist', + buildDir: 'my-build', + empty: false, + transformAliasedImportPathsInCollection: true, + }; + userConfig.outputTargets = [outputTarget]; + userConfig.buildDist = true; + + const { config } = validateConfig(userConfig, mockLoadConfigInit()); + + expect(config.outputTargets).toEqual([ + { + buildDir: path.join(rootDir, 'my-dist', 'my-build'), + collectionDir: path.join(rootDir, 'my-dist', 'collection'), + copy: [], + dir: path.join(rootDir, 'my-dist'), + empty: false, + esmLoaderPath: path.join(rootDir, 'my-dist', 'loader'), + type: 'dist', + polyfills: undefined, + typesDir: path.join(rootDir, 'my-dist', 'types'), + transformAliasedImportPathsInCollection: true, + }, + { + esmDir: path.join(rootDir, 'my-dist', 'my-build', 'testing'), + empty: false, + isBrowserBuild: true, + legacyLoaderFile: path.join(rootDir, 'my-dist', 'my-build', 'testing.js'), + polyfills: true, + systemDir: undefined, + systemLoaderFile: undefined, + type: 'dist-lazy', + }, + { + copyAssets: 'dist', + copy: [], + dir: path.join(rootDir, 'my-dist', 'my-build', 'testing'), + type: 'copy', + }, + { + file: path.join(rootDir, 'my-dist', 'my-build', 'testing', 'testing.css'), + type: 'dist-global-styles', + }, + { + dir: path.join(rootDir, 'my-dist'), + type: 'dist-types', + typesDir: path.join(rootDir, 'my-dist', 'types'), + }, + { + collectionDir: path.join(rootDir, 'my-dist', 'collection'), + dir: path.join(rootDir, '/my-dist'), + empty: false, + transformAliasedImportPaths: true, + type: 'dist-collection', + }, + { + copy: [{ src: '**/*.svg' }, { src: '**/*.js' }], + copyAssets: 'collection', + dir: path.join(rootDir, 'my-dist', 'collection'), + type: 'copy', + }, + { + type: 'dist-lazy', + cjsDir: path.join(rootDir, 'my-dist', 'cjs'), + cjsIndexFile: path.join(rootDir, 'my-dist', 'index.cjs.js'), + empty: false, + esmDir: path.join(rootDir, 'my-dist', 'esm'), + esmEs5Dir: undefined, + esmIndexFile: path.join(rootDir, 'my-dist', 'index.js'), + polyfills: true, + }, + { + cjsDir: path.join(rootDir, 'my-dist', 'cjs'), + componentDts: path.join(rootDir, 'my-dist', 'types', 'components.d.ts'), + dir: path.join(rootDir, 'my-dist', 'loader'), + empty: false, + esmDir: path.join(rootDir, 'my-dist', 'esm'), + esmEs5Dir: undefined, + type: 'dist-lazy-loader', + }, + ]); + }); }); diff --git a/src/compiler/output-targets/dist-collection/index.ts b/src/compiler/output-targets/dist-collection/index.ts index e240424afc6..02e30890f3d 100644 --- a/src/compiler/output-targets/dist-collection/index.ts +++ b/src/compiler/output-targets/dist-collection/index.ts @@ -3,7 +3,20 @@ import { catchError, COLLECTION_MANIFEST_FILE_NAME, flatOne, generatePreamble, n import { isOutputTargetDistCollection } from '../output-utils'; import { join, relative } from 'path'; import { typescriptVersion, version } from '../../../version'; +import ts from 'typescript'; +import { mapImportsToPathAliases } from '../../transformers/map-imports-to-path-aliases'; +/** + * Main output target function for `dist-collection`. This function takes the compiled output from a + * {@link ts.Program}, runs each file through a transformer to transpile import path aliases, and then writes + * the output code and source maps to disk in the specified collection directory. + * + * @param config The validated Stencil config. + * @param compilerCtx The current compiler context. + * @param buildCtx The current build context. + * @param changedModuleFiles The changed modules returned from the TS compiler. + * @returns An empty promise. Resolved once all functions finish. + */ export const outputCollection = async ( config: d.ValidatedConfig, compilerCtx: d.CompilerCtx, @@ -27,15 +40,31 @@ export const outputCollection = async ( const mapCode = mod.sourceMapFileText; await Promise.all( - outputTargets.map(async (o) => { + outputTargets.map(async (target) => { const relPath = relative(config.srcDir, mod.jsFilePath); - const filePath = join(o.collectionDir, relPath); - await compilerCtx.fs.writeFile(filePath, code, { outputTargetType: o.type }); + const filePath = join(target.collectionDir, relPath); + + // Transpile the already transpiled modules to apply + // a transformer to convert aliased import paths to relative paths + // We run this even if the transformer will perform no action + // to avoid race conditions between multiple output targets that + // may be writing to the same location + const { outputText } = ts.transpileModule(code, { + fileName: mod.sourceFilePath, + compilerOptions: { + target: ts.ScriptTarget.Latest, + }, + transformers: { + after: [mapImportsToPathAliases(config, filePath, target)], + }, + }); + + await compilerCtx.fs.writeFile(filePath, outputText, { outputTargetType: target.type }); if (mod.sourceMapPath) { const relativeSourceMapPath = relative(config.srcDir, mod.sourceMapPath); - const sourceMapOutputFilePath = join(o.collectionDir, relativeSourceMapPath); - await compilerCtx.fs.writeFile(sourceMapOutputFilePath, mapCode, { outputTargetType: o.type }); + const sourceMapOutputFilePath = join(target.collectionDir, relativeSourceMapPath); + await compilerCtx.fs.writeFile(sourceMapOutputFilePath, mapCode, { outputTargetType: target.type }); } }) ); diff --git a/src/compiler/output-targets/test/output-targets-collection.spec.ts b/src/compiler/output-targets/test/output-targets-collection.spec.ts new file mode 100644 index 00000000000..73358d264ff --- /dev/null +++ b/src/compiler/output-targets/test/output-targets-collection.spec.ts @@ -0,0 +1,73 @@ +import { outputCollection } from '../dist-collection'; +import type * as d from '../../../declarations'; +import { mockValidatedConfig, mockBuildCtx, mockCompilerCtx, mockModule } from '@stencil/core/testing'; +import * as test from '../../transformers/map-imports-to-path-aliases'; +import { normalize } from 'path'; + +describe('Dist Collection output target', () => { + let mockConfig: d.ValidatedConfig; + let mockedBuildCtx: d.BuildCtx; + let mockedCompilerCtx: d.CompilerCtx; + let changedModules: d.Module[]; + + let mapImportPathSpy: jest.SpyInstance; + + const mockTraverse = jest.fn().mockImplementation((source: any) => source); + const mockMap = jest.fn().mockImplementation(() => mockTraverse); + const target: d.OutputTargetDistCollection = { + type: 'dist-collection', + dir: '', + collectionDir: '/dist/collection', + }; + + beforeEach(() => { + mockConfig = mockValidatedConfig({ + srcDir: '/src', + }); + mockedBuildCtx = mockBuildCtx(); + mockedCompilerCtx = mockCompilerCtx(); + changedModules = [ + mockModule({ + staticSourceFileText: '', + jsFilePath: '/src/main.js', + sourceFilePath: '/src/main.ts', + }), + ]; + + jest.spyOn(mockedCompilerCtx.fs, 'writeFile'); + + mapImportPathSpy = jest.spyOn(test, 'mapImportsToPathAliases'); + mapImportPathSpy.mockReturnValue(mockMap); + }); + + afterEach(() => { + jest.restoreAllMocks(); + }); + + describe('transform aliased import paths', () => { + // These tests ensure that the transformer for import paths is called regardless + // of the config value (the function will decided whether or not to actually do anything) to avoid + // a race condition with duplicate file writes + it.each([true, false])( + 'calls function to transform aliased import paths when the output target config flag is `%s`', + async (transformAliasedImportPaths: boolean) => { + mockConfig.outputTargets = [ + { + ...target, + transformAliasedImportPaths, + }, + ]; + + await outputCollection(mockConfig, mockedCompilerCtx, mockedBuildCtx, changedModules); + + expect(mapImportPathSpy).toHaveBeenCalledWith(mockConfig, normalize('/dist/collection/main.js'), { + collectionDir: '/dist/collection', + dir: '', + transformAliasedImportPaths, + type: 'dist-collection', + }); + expect(mapImportPathSpy).toHaveBeenCalledTimes(1); + } + ); + }); +}); diff --git a/src/compiler/transformers/map-imports-to-path-aliases.ts b/src/compiler/transformers/map-imports-to-path-aliases.ts new file mode 100644 index 00000000000..ce3df3e8e50 --- /dev/null +++ b/src/compiler/transformers/map-imports-to-path-aliases.ts @@ -0,0 +1,95 @@ +import { normalizePath } from '@utils'; +import { dirname, relative } from 'path'; +import ts from 'typescript'; +import type * as d from '../../declarations'; + +/** + * This method is responsible for replacing user-defined import path aliases ({@link https://www.typescriptlang.org/docs/handbook/module-resolution.html#path-mapping}) + * with generated relative import paths during the transformation step of the TS compilation process. + * This action is taken to prevent issues with import paths not being transpiled at build time resulting in + * unknown imports in output code for some output targets (`dist-collection` for instance). Output targets that do not run through a bundler + * are unable to resolve imports using the aliased path names and TS intentionally does not replace resolved paths as a part of + * their compiler ({@link https://github.com/microsoft/TypeScript/issues/10866}) + * + * @param config The Stencil configuration object. + * @param destinationFilePath The location on disk the file will be written to. + * @param outputTarget The configuration for the collection output target. + * @returns A factory for creating a {@link ts.Transformer}. + */ +export const mapImportsToPathAliases = ( + config: d.ValidatedConfig, + destinationFilePath: string, + outputTarget: d.OutputTargetDistCollection +): ts.TransformerFactory => { + return (transformCtx) => { + const compilerHost = ts.createCompilerHost(config.tsCompilerOptions); + + const visit = + (sourceFile: string) => + (node: ts.Node): ts.VisitResult => { + // Only transform paths when the `transformAliasedImportPaths` flag is + // set on the output target config + // + // We should only attempt to transform standard module imports: + // - import * as ts from 'typescript'; + // - import { Foo, Bar } from 'baz'; + // - import { Foo as Bar } from 'baz'; + // - import Foo from 'bar'; + // We should NOT transform other import declaration types: + // - import a = Foo.Bar + if ( + outputTarget.transformAliasedImportPaths && + ts.isImportDeclaration(node) && + ts.isStringLiteral(node.moduleSpecifier) + ) { + let importPath = node.moduleSpecifier.text; + + // We will ignore transforming any paths that are already relative paths or + // imports from external modules/packages + if (!importPath.startsWith('.')) { + const module = ts.resolveModuleName(importPath, sourceFile, config.tsCompilerOptions, compilerHost); + + const hasResolvedFileName = module.resolvedModule?.resolvedFileName != null; + const isModuleFromNodeModules = module.resolvedModule?.isExternalLibraryImport === true; + const shouldTranspileImportPath = hasResolvedFileName && !isModuleFromNodeModules; + + if (shouldTranspileImportPath) { + // Create a regular expression that will be used to remove the last file extension + // from the import path + const extensionRegex = new RegExp( + Object.values(ts.Extension) + .map((extension) => `${extension}$`) + .join('|') + ); + + // In order to make sure the relative path works when the destination depth is different than the source + // file structure depth, we need to determine where the resolved file exists relative to the destination directory + const resolvePathInDestination = module.resolvedModule.resolvedFileName.replace( + config.srcDir, + outputTarget.collectionDir + ); + + importPath = normalizePath( + relative(dirname(destinationFilePath), resolvePathInDestination).replace(extensionRegex, '') + ); + } + } + + return transformCtx.factory.updateImportDeclaration( + node, + node.decorators, + node.modifiers, + node.importClause, + transformCtx.factory.createStringLiteral(importPath), + node.assertClause + ); + } + + return ts.visitEachChild(node, visit(sourceFile), transformCtx); + }; + + return (tsSourceFile) => { + return ts.visitEachChild(tsSourceFile, visit(tsSourceFile.fileName), transformCtx); + }; + }; +}; diff --git a/src/compiler/transformers/test/map-imports-to-path-aliases.spec.ts b/src/compiler/transformers/test/map-imports-to-path-aliases.spec.ts new file mode 100644 index 00000000000..23c1cc8dc4d --- /dev/null +++ b/src/compiler/transformers/test/map-imports-to-path-aliases.spec.ts @@ -0,0 +1,180 @@ +import { mockValidatedConfig } from '@stencil/core/testing'; +import type { OutputTargetDistCollection } from '@stencil/core/declarations'; +import { ValidatedConfig } from '../../../internal'; +import { transpileModule } from './transpile'; +import ts, { Extension } from 'typescript'; +import { mapImportsToPathAliases } from '../map-imports-to-path-aliases'; + +describe('mapImportsToPathAliases', () => { + let module: ReturnType; + let config: ValidatedConfig; + let resolveModuleNameSpy: jest.SpyInstance< + ReturnType, + Parameters + >; + let outputTarget: OutputTargetDistCollection; + + beforeEach(() => { + config = mockValidatedConfig({ tsCompilerOptions: {} }); + + resolveModuleNameSpy = jest.spyOn(ts, 'resolveModuleName'); + + outputTarget = { + type: 'dist-collection', + dir: 'dist', + collectionDir: 'dist/collection', + transformAliasedImportPaths: true, + }; + }); + + afterEach(() => { + resolveModuleNameSpy.mockReset(); + }); + + it('does nothing if the config flag is `false`', () => { + outputTarget.transformAliasedImportPaths = false; + resolveModuleNameSpy.mockReturnValue({ + resolvedModule: { + isExternalLibraryImport: false, + extension: Extension.Ts, + resolvedFileName: 'utils.js', + }, + }); + const inputText = ` + import { utils } from "@utils/utils"; + + utils.test(); + `; + + module = transpileModule(inputText, config, null, [], [mapImportsToPathAliases(config, '', outputTarget)]); + + expect(module.outputText).toContain('import { utils } from "@utils/utils";'); + }); + + it('ignores relative imports', () => { + resolveModuleNameSpy.mockReturnValue({ + resolvedModule: { + isExternalLibraryImport: false, + extension: Extension.Ts, + resolvedFileName: 'utils.js', + }, + }); + const inputText = ` + import * as dateUtils from "../utils"; + + dateUtils.test(); + `; + + module = transpileModule(inputText, config, null, [], [mapImportsToPathAliases(config, '', outputTarget)]); + + expect(module.outputText).toContain('import * as dateUtils from "../utils";'); + }); + + it('ignores external imports', () => { + resolveModuleNameSpy.mockReturnValue({ + resolvedModule: { + isExternalLibraryImport: true, + extension: Extension.Ts, + resolvedFileName: 'utils.js', + }, + }); + const inputText = ` + import { utils } from "@stencil/core"; + + utils.test(); + `; + + module = transpileModule(inputText, config, null, [], [mapImportsToPathAliases(config, '', outputTarget)]); + + expect(module.outputText).toContain('import { utils } from "@stencil/core";'); + }); + + it('does nothing if there is no resolved module', () => { + resolveModuleNameSpy.mockReturnValue({ + resolvedModule: undefined, + }); + const inputText = ` + import { utils } from "@utils"; + + utils.test(); + `; + + module = transpileModule(inputText, config, null, [], [mapImportsToPathAliases(config, '', outputTarget)]); + + expect(module.outputText).toContain('import { utils } from "@utils";'); + }); + + // TODO(STENCIL-223): remove spy to test actual resolution behavior + it('replaces the path alias with the generated relative path', () => { + resolveModuleNameSpy.mockReturnValue({ + resolvedModule: { + isExternalLibraryImport: false, + extension: Extension.Ts, + resolvedFileName: 'utils.ts', + }, + }); + const inputText = ` + import { utils } from "@utils"; + + utils.test(); + `; + + module = transpileModule(inputText, config, null, [], [mapImportsToPathAliases(config, '', outputTarget)]); + + expect(module.outputText).toContain('import { utils } from "utils";'); + }); + + // The resolved module is not part of the output directory + it('generates the correct relative path when the resolved module is outside the transpiled project', () => { + config.srcDir = '/test-dir'; + resolveModuleNameSpy.mockReturnValue({ + resolvedModule: { + isExternalLibraryImport: false, + extension: Extension.Ts, + resolvedFileName: '/some-compiled-dir/utils/utils.ts', + }, + }); + const inputText = ` + import { utils } from "@utils"; + + utils.test(); + `; + + module = transpileModule( + inputText, + config, + null, + [], + [mapImportsToPathAliases(config, '/dist/collection/test.js', outputTarget)] + ); + + expect(module.outputText).toContain(`import { utils } from "../../some-compiled-dir/utils/utils";`); + }); + + // Source module and resolved module are in the same output directory + it('generates the correct relative path when the resolved module is within the transpiled project', () => { + config.srcDir = '/test-dir'; + resolveModuleNameSpy.mockReturnValue({ + resolvedModule: { + isExternalLibraryImport: false, + extension: Extension.Ts, + resolvedFileName: '/test-dir/utils/utils.ts', + }, + }); + const inputText = ` + import { utils } from "@utils"; + + utils.test(); + `; + + module = transpileModule( + inputText, + config, + null, + [], + [mapImportsToPathAliases(config, 'dist/collection/test.js', outputTarget)] + ); + + expect(module.outputText).toContain(`import { utils } from "./utils/utils";`); + }); +}); diff --git a/src/compiler/transformers/test/transpile.ts b/src/compiler/transformers/test/transpile.ts index 2e8e886fee1..11b53e876f0 100644 --- a/src/compiler/transformers/test/transpile.ts +++ b/src/compiler/transformers/test/transpile.ts @@ -17,8 +17,8 @@ import { getScriptTarget } from '../transform-utils'; */ export function transpileModule( input: string, - config?: d.Config, - compilerCtx?: d.CompilerCtx, + config?: d.Config | null, + compilerCtx?: d.CompilerCtx | null, beforeTransformers: ts.TransformerFactory[] = [], afterTransformers: ts.TransformerFactory[] = [] ) { @@ -154,12 +154,12 @@ export function transpileModule( } export function getStaticGetter(output: string, prop: string) { - const toEvaludate = `return ${output.replace('export', '')}`; + const toEvaluate = `return ${output.replace('export', '')}`; try { - const Obj = new Function(toEvaludate); + const Obj = new Function(toEvaluate); return Obj()[prop]; } catch (e) { console.error(e); - console.error(toEvaludate); + console.error(toEvaluate); } } diff --git a/src/declarations/stencil-public-compiler.ts b/src/declarations/stencil-public-compiler.ts index ec320500b48..907715ce344 100644 --- a/src/declarations/stencil-public-compiler.ts +++ b/src/declarations/stencil-public-compiler.ts @@ -1846,6 +1846,28 @@ export interface OutputTargetDist extends OutputTargetBase { dir?: string; collectionDir?: string | null; + /** + * When `true` this flag will transform aliased import paths defined in + * a project's `tsconfig.json` to relative import paths in the compiled output's + * `dist-collection` bundle if it is generated (i.e. `collectionDir` is set). + * + * Paths will be left in aliased format if `false` or `undefined`. + * + * @example + * // tsconfig.json + * { + * paths: { + * "@utils/*": ['/src/utils/*'] + * } + * } + * + * // Source file + * import * as dateUtils from '@utils/date-utils'; + * // Output file + * import * as dateUtils from '../utils/date-utils'; + */ + transformAliasedImportPathsInCollection?: boolean | null; + typesDir?: string; esmLoaderPath?: string; copy?: CopyTask[]; @@ -1859,6 +1881,26 @@ export interface OutputTargetDistCollection extends OutputTargetBase { empty?: boolean; dir: string; collectionDir: string; + /** + * When `true` this flag will transform aliased import paths defined in + * a project's `tsconfig.json` to relative import paths in the compiled output. + * + * Paths will be left in aliased format if `false` or `undefined`. + * + * @example + * // tsconfig.json + * { + * paths: { + * "@utils/*": ['/src/utils/*'] + * } + * } + * + * // Source file + * import * as dateUtils from '@utils/date-utils'; + * // Output file + * import * as dateUtils from '../utils/date-utils'; + */ + transformAliasedImportPaths?: boolean | null; } export interface OutputTargetDistTypes extends OutputTargetBase {