-
Notifications
You must be signed in to change notification settings - Fork 795
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(compiler): generate export maps on build (#5809)
* generate export maps in package.json on build * npm cli commands * add config flag & generation conditionals * wip(tests) * add tests * remove log * account for primary output target * type comment & SNCs
- Loading branch information
1 parent
3da736d
commit b6d2404
Showing
6 changed files
with
265 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,143 @@ | ||
import { mockBuildCtx, mockValidatedConfig } from '@stencil/core/testing'; | ||
import childProcess from 'child_process'; | ||
|
||
import * as d from '../../../declarations'; | ||
import { stubComponentCompilerMeta } from '../../types/tests/ComponentCompilerMeta.stub'; | ||
import { writeExportMaps } from '../write-export-maps'; | ||
|
||
describe('writeExportMaps', () => { | ||
let config: d.ValidatedConfig; | ||
let buildCtx: d.BuildCtx; | ||
let execSyncSpy: jest.SpyInstance; | ||
|
||
beforeEach(() => { | ||
config = mockValidatedConfig(); | ||
buildCtx = mockBuildCtx(config); | ||
|
||
execSyncSpy = jest.spyOn(childProcess, 'execSync').mockImplementation(() => ''); | ||
}); | ||
|
||
afterEach(() => { | ||
jest.clearAllMocks(); | ||
}); | ||
|
||
it('should not generate any exports if there are no output targets', () => { | ||
writeExportMaps(config, buildCtx); | ||
|
||
expect(execSyncSpy).toHaveBeenCalledTimes(0); | ||
}); | ||
|
||
it('should generate the default exports for the lazy build if present', () => { | ||
config.outputTargets = [ | ||
{ | ||
type: 'dist', | ||
dir: '/dist', | ||
typesDir: '/dist/types', | ||
}, | ||
]; | ||
|
||
writeExportMaps(config, buildCtx); | ||
|
||
expect(execSyncSpy).toHaveBeenCalledTimes(3); | ||
expect(execSyncSpy).toHaveBeenCalledWith(`npm pkg set "exports[.][import]"="./dist/index.js"`); | ||
expect(execSyncSpy).toHaveBeenCalledWith(`npm pkg set "exports[.][require]"="./dist/index.cjs.js"`); | ||
expect(execSyncSpy).toHaveBeenCalledWith(`npm pkg set "exports[.][types]"="./dist/types/index.d.ts"`); | ||
}); | ||
|
||
it('should generate the default exports for the custom elements build if present', () => { | ||
config.outputTargets = [ | ||
{ | ||
type: 'dist-custom-elements', | ||
dir: '/dist/components', | ||
generateTypeDeclarations: true, | ||
}, | ||
]; | ||
|
||
writeExportMaps(config, buildCtx); | ||
|
||
expect(execSyncSpy).toHaveBeenCalledTimes(2); | ||
expect(execSyncSpy).toHaveBeenCalledWith(`npm pkg set "exports[.][import]"="./dist/components/index.js"`); | ||
expect(execSyncSpy).toHaveBeenCalledWith(`npm pkg set "exports[.][types]"="./dist/components/index.d.ts"`); | ||
}); | ||
|
||
it('should generate the lazy loader exports if the output target is present', () => { | ||
config.rootDir = '/'; | ||
config.outputTargets.push({ | ||
type: 'dist-lazy-loader', | ||
dir: '/dist/lazy-loader', | ||
empty: true, | ||
esmDir: '/dist/esm', | ||
cjsDir: '/dist/cjs', | ||
componentDts: '/dist/components.d.ts', | ||
}); | ||
|
||
writeExportMaps(config, buildCtx); | ||
|
||
expect(execSyncSpy).toHaveBeenCalledTimes(3); | ||
expect(execSyncSpy).toHaveBeenCalledWith(`npm pkg set "exports[./loader][import]"="./dist/lazy-loader/index.js"`); | ||
expect(execSyncSpy).toHaveBeenCalledWith(`npm pkg set "exports[./loader][require]"="./dist/lazy-loader/index.cjs"`); | ||
expect(execSyncSpy).toHaveBeenCalledWith(`npm pkg set "exports[./loader][types]"="./dist/lazy-loader/index.d.ts"`); | ||
}); | ||
|
||
it('should generate the custom elements exports if the output target is present', () => { | ||
config.rootDir = '/'; | ||
config.outputTargets.push({ | ||
type: 'dist-custom-elements', | ||
dir: '/dist/components', | ||
generateTypeDeclarations: true, | ||
}); | ||
|
||
buildCtx.components = [ | ||
stubComponentCompilerMeta({ | ||
tagName: 'my-component', | ||
componentClassName: 'MyComponent', | ||
}), | ||
]; | ||
|
||
writeExportMaps(config, buildCtx); | ||
|
||
expect(execSyncSpy).toHaveBeenCalledTimes(4); | ||
expect(execSyncSpy).toHaveBeenCalledWith( | ||
`npm pkg set "exports[./my-component][import]"="./dist/components/my-component.js"`, | ||
); | ||
expect(execSyncSpy).toHaveBeenCalledWith( | ||
`npm pkg set "exports[./my-component][types]"="./dist/components/my-component.d.ts"`, | ||
); | ||
}); | ||
|
||
it('should generate the custom elements exports for multiple components', () => { | ||
config.rootDir = '/'; | ||
config.outputTargets.push({ | ||
type: 'dist-custom-elements', | ||
dir: '/dist/components', | ||
generateTypeDeclarations: true, | ||
}); | ||
|
||
buildCtx.components = [ | ||
stubComponentCompilerMeta({ | ||
tagName: 'my-component', | ||
componentClassName: 'MyComponent', | ||
}), | ||
stubComponentCompilerMeta({ | ||
tagName: 'my-other-component', | ||
componentClassName: 'MyOtherComponent', | ||
}), | ||
]; | ||
|
||
writeExportMaps(config, buildCtx); | ||
|
||
expect(execSyncSpy).toHaveBeenCalledTimes(6); | ||
expect(execSyncSpy).toHaveBeenCalledWith( | ||
`npm pkg set "exports[./my-component][import]"="./dist/components/my-component.js"`, | ||
); | ||
expect(execSyncSpy).toHaveBeenCalledWith( | ||
`npm pkg set "exports[./my-component][types]"="./dist/components/my-component.d.ts"`, | ||
); | ||
expect(execSyncSpy).toHaveBeenCalledWith( | ||
`npm pkg set "exports[./my-other-component][import]"="./dist/components/my-other-component.js"`, | ||
); | ||
expect(execSyncSpy).toHaveBeenCalledWith( | ||
`npm pkg set "exports[./my-other-component][types]"="./dist/components/my-other-component.d.ts"`, | ||
); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
import { | ||
isEligiblePrimaryPackageOutputTarget, | ||
isOutputTargetDistCustomElements, | ||
isOutputTargetDistLazyLoader, | ||
} from '@utils'; | ||
import { relative } from '@utils'; | ||
import { execSync } from 'child_process'; | ||
|
||
import * as d from '../../declarations'; | ||
import { PRIMARY_PACKAGE_TARGET_CONFIGS } from '../types/validate-primary-package-output-target'; | ||
|
||
/** | ||
* Create export map entry point definitions for the `package.json` file using the npm CLI. | ||
* This will generate a root entry point for the package, as well as entry points for each component and | ||
* the lazy loader (if applicable). | ||
* | ||
* @param config The validated Stencil config | ||
* @param buildCtx The build context containing the components to generate export maps for | ||
*/ | ||
export const writeExportMaps = (config: d.ValidatedConfig, buildCtx: d.BuildCtx) => { | ||
const eligiblePrimaryTargets = config.outputTargets.filter(isEligiblePrimaryPackageOutputTarget); | ||
if (eligiblePrimaryTargets.length > 0) { | ||
const primaryTarget = | ||
eligiblePrimaryTargets.find((o) => o.isPrimaryPackageOutputTarget) ?? eligiblePrimaryTargets[0]; | ||
const outputTargetConfig = PRIMARY_PACKAGE_TARGET_CONFIGS[primaryTarget.type]; | ||
|
||
if (outputTargetConfig.getModulePath) { | ||
const importPath = outputTargetConfig.getModulePath(config.rootDir, primaryTarget.dir!); | ||
|
||
if (importPath) { | ||
execSync(`npm pkg set "exports[.][import]"="${importPath}"`); | ||
} | ||
} | ||
|
||
if (outputTargetConfig.getMainPath) { | ||
const requirePath = outputTargetConfig.getMainPath(config.rootDir, primaryTarget.dir!); | ||
|
||
if (requirePath) { | ||
execSync(`npm pkg set "exports[.][require]"="${requirePath}"`); | ||
} | ||
} | ||
|
||
if (outputTargetConfig.getTypesPath) { | ||
const typesPath = outputTargetConfig.getTypesPath(config.rootDir, primaryTarget); | ||
|
||
if (typesPath) { | ||
execSync(`npm pkg set "exports[.][types]"="${typesPath}"`); | ||
} | ||
} | ||
} | ||
|
||
const distLazyLoader = config.outputTargets.find(isOutputTargetDistLazyLoader); | ||
if (distLazyLoader != null) { | ||
// Calculate relative path from project root to lazy-loader output directory | ||
let outDir = relative(config.rootDir, distLazyLoader.dir); | ||
if (!outDir.startsWith('.')) { | ||
outDir = './' + outDir; | ||
} | ||
|
||
execSync(`npm pkg set "exports[./loader][import]"="${outDir}/index.js"`); | ||
execSync(`npm pkg set "exports[./loader][require]"="${outDir}/index.cjs"`); | ||
execSync(`npm pkg set "exports[./loader][types]"="${outDir}/index.d.ts"`); | ||
} | ||
|
||
const distCustomElements = config.outputTargets.find(isOutputTargetDistCustomElements); | ||
if (distCustomElements != null) { | ||
// Calculate relative path from project root to custom elements output directory | ||
let outDir = relative(config.rootDir, distCustomElements.dir!); | ||
if (!outDir.startsWith('.')) { | ||
outDir = './' + outDir; | ||
} | ||
|
||
buildCtx.components.forEach((cmp) => { | ||
execSync(`npm pkg set "exports[./${cmp.tagName}][import]"="${outDir}/${cmp.tagName}.js"`); | ||
|
||
if (distCustomElements.generateTypeDeclarations) { | ||
execSync(`npm pkg set "exports[./${cmp.tagName}][types]"="${outDir}/${cmp.tagName}.d.ts"`); | ||
} | ||
}); | ||
} | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters