Skip to content

Commit 509869c

Browse files
tanner-reitsrwaskiewicz
authored andcommitted
feat(compiler): export custom types in compiled output (#3710)
NOTE: This was originally introduced as a feature in Stencil 2.x, but was reverted when a breaking change was identified. Initial PR (orginally merged as a part of Stencil 2.x): #3612 Reversion of above PR (due to identified breaking change): #3708 Types for custom events are now re-exported from the `components.d.ts` file. This allows those types to be easily accessed from the root of the type distribution (so long as the `index.ts` file is set to export all definitions from the `components.d.ts` file): `import { MyCustomType } from '@my-lib/types';` Or, when using `dist-custom-elements`, these types can now be accessed from the custom element output: `import { MyCustomType } from '@my-custom-elements-output';` BREAKING CHANGE: Any project that currently manually maintains a means of creating type definitions for types created as a part of the Stencil project will either simply need to remove that manual step from their development or update it to re-export the types exported from the generated `component.d.ts` file
1 parent 650a355 commit 509869c

File tree

9 files changed

+1462
-28
lines changed

9 files changed

+1462
-28
lines changed

src/compiler/output-targets/dist-custom-elements/custom-elements-types.ts

+4
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,10 @@ const generateCustomElementsTypesOutput = async (
114114
// entities exported there, we will re-export the typedefs iff
115115
// the `customElementsExportBehavior` is set to barrel component exports
116116
if (isBarrelExport) {
117+
// If there is an `index.ts` file in the src directory, we'll re-export anything
118+
// exported from that file
119+
// Otherwise, we'll export everything from the auto-generated `components.d.ts`
120+
// file in the output directory
117121
const usersIndexJsPath = join(config.srcDir, 'index.ts');
118122
const hasUserIndex = await compilerCtx.fs.access(usersIndexJsPath);
119123
if (hasUserIndex) {

src/compiler/sys/stencil-sys.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -301,12 +301,12 @@ export const createSystem = (c?: { logger?: Logger }): CompilerSystem => {
301301
error: null,
302302
};
303303

304-
remoreDirSyncRecursive(p, opts, results);
304+
removeDirSyncRecursive(p, opts, results);
305305

306306
return results;
307307
};
308308

309-
const remoreDirSyncRecursive = (
309+
const removeDirSyncRecursive = (
310310
p: string,
311311
opts: CompilerSystemRemoveDirectoryOptions,
312312
results: CompilerSystemRemoveDirectoryResults
@@ -321,7 +321,7 @@ export const createSystem = (c?: { logger?: Logger }): CompilerSystem => {
321321
const item = items.get(dirItemPath);
322322
if (item) {
323323
if (item.isDirectory) {
324-
remoreDirSyncRecursive(dirItemPath, opts, results);
324+
removeDirSyncRecursive(dirItemPath, opts, results);
325325
} else if (item.isFile) {
326326
const removeFileResults = removeFileSync(dirItemPath);
327327
if (removeFileResults.error) {

src/compiler/transformers/test/parse-events.spec.ts

+1
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ describe('parse events', () => {
105105
references: {
106106
Mode: {
107107
location: 'local',
108+
path: 'module.tsx',
108109
},
109110
},
110111
});

src/compiler/transformers/transform-utils.ts

+9
Original file line numberDiff line numberDiff line change
@@ -513,6 +513,15 @@ const getTypeReferenceLocation = (typeName: string, tsNode: ts.Node): d.Componen
513513
if (isExported) {
514514
return {
515515
location: 'local',
516+
// If this is a local import, we know the path to the type
517+
// is the same as the current source file path
518+
//
519+
// We need to explicitly include the path here because
520+
// future logic for generating app types will use this resolved reference
521+
// to ensure that type name collisions do no occur in the output type
522+
// declaration file. If this path is omitted, the correct aliased type names
523+
// will not be used for component event definitions
524+
path: sourceFileObj.fileName,
516525
};
517526
}
518527

src/compiler/transpile/run-program.ts

+1
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ export const runTsProgram = async (
6161
// Finalize components metadata
6262
buildCtx.moduleFiles = Array.from(compilerCtx.moduleMap.values());
6363
buildCtx.components = getComponentsFromModules(buildCtx.moduleFiles);
64+
6465
updateComponentBuildConditionals(compilerCtx.moduleMap, buildCtx.components);
6566
resolveComponentDependencies(buildCtx.components);
6667

src/compiler/types/generate-app-types.ts

+26-25
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ import { updateReferenceTypeImports } from './update-import-refs';
1919
* @returns `true` if the type declaration file written to disk has changed, `false` otherwise
2020
*/
2121
export const generateAppTypes = async (
22-
config: d.Config,
22+
config: d.ValidatedConfig,
2323
compilerCtx: d.CompilerCtx,
2424
buildCtx: d.BuildCtx,
2525
destination: string
@@ -44,7 +44,7 @@ export const generateAppTypes = async (
4444
);
4545
}
4646

47-
const writeResults = await compilerCtx.fs.writeFile(componentsDtsFilePath, componentTypesFileContent, {
47+
const writeResults = await compilerCtx.fs.writeFile(normalizePath(componentsDtsFilePath), componentTypesFileContent, {
4848
immediateWrite: true,
4949
});
5050
const hasComponentsDtsChanged = writeResults.changedContent;
@@ -92,29 +92,30 @@ const generateComponentTypesFile = (config: d.Config, buildCtx: d.BuildCtx, areT
9292
c.push(COMPONENTS_DTS_HEADER);
9393
c.push(`import { HTMLStencilElement, JSXBase } from "@stencil/core/internal";`);
9494

95-
// write the import statements for our type declaration file
96-
c.push(
97-
...Object.keys(typeImportData).map((filePath) => {
98-
const typeData = typeImportData[filePath];
99-
let importFilePath: string;
100-
if (isAbsolute(filePath)) {
101-
importFilePath = normalizePath('./' + relative(config.srcDir, filePath)).replace(/\.(tsx|ts)$/, '');
102-
} else {
103-
importFilePath = filePath;
104-
}
105-
106-
return `import { ${typeData
107-
.sort(sortImportNames)
108-
.map((td) => {
109-
if (td.localName === td.importName) {
110-
return `${td.importName}`;
111-
} else {
112-
return `${td.localName} as ${td.importName}`;
113-
}
114-
})
115-
.join(`, `)} } from "${importFilePath}";`;
116-
})
117-
);
95+
// Map event type metadata to partial expressions (omitting import/export keywords)
96+
// e.g. { TestEvent } from '../path/to/event/test-event.interface';
97+
const expressions = Object.keys(typeImportData).map((filePath) => {
98+
const typeData = typeImportData[filePath];
99+
100+
let importFilePath = filePath;
101+
if (isAbsolute(filePath)) {
102+
importFilePath = normalizePath('./' + relative(config.srcDir, filePath)).replace(/\.(tsx|ts)$/, '');
103+
}
104+
105+
return `{ ${typeData
106+
.sort(sortImportNames)
107+
.map((td) => {
108+
if (td.localName === td.importName) {
109+
return `${td.importName}`;
110+
} else {
111+
return `${td.localName} as ${td.importName}`;
112+
}
113+
})
114+
.join(`, `)} } from "${importFilePath}";`;
115+
});
116+
117+
// Write all import and export statements for event types
118+
c.push(...expressions.map((ref) => `import ${ref}`), ...expressions.map((ref) => `export ${ref}`));
118119

119120
c.push(`export namespace Components {`);
120121
c.push(...modules.map((m) => `${m.component}`));

0 commit comments

Comments
 (0)