Skip to content

Commit

Permalink
fix(compiler): components typedef path aliases (#4365)
Browse files Browse the repository at this point in the history
* transform path aliases in `components.d.ts` files

* only resolve potential aliased modules, ignore relative paths

* add todo comment for test updates

* fix SNC violations

* only transform path aliases if behavior is enabled in config

* add tests for config flag check

* use actual TS module resolution

* quick PR fixes
  • Loading branch information
tanner-reits committed May 16, 2023
1 parent 688aba5 commit fd63c17
Show file tree
Hide file tree
Showing 3 changed files with 246 additions and 26 deletions.
2 changes: 1 addition & 1 deletion src/compiler/types/generate-app-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ const generateComponentTypesFile = (config: d.Config, buildCtx: d.BuildCtx, areT
* data structure for each Stencil component in series, therefore the memory footprint of this entity will likely
* grow as more components (with additional types) are processed.
*/
typeImportData = updateReferenceTypeImports(typeImportData, allTypes, cmp, cmp.sourceFilePath);
typeImportData = updateReferenceTypeImports(typeImportData, allTypes, cmp, cmp.sourceFilePath, config);
if (cmp.events.length > 0) {
/**
* Only generate event detail types for components that have events.
Expand Down
235 changes: 215 additions & 20 deletions src/compiler/types/tests/generate-app-types.spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { mockBuildCtx, mockCompilerCtx, mockValidatedConfig } from '@stencil/core/testing';
import path from 'path';

import type * as d from '../../../declarations';
import { patchTypescript } from '../../sys/typescript/typescript-sys';
import { generateAppTypes } from '../generate-app-types';
import { stubComponentCompilerEvent } from './ComponentCompilerEvent.stub';
import { stubComponentCompilerMeta } from './ComponentCompilerMeta.stub';
Expand All @@ -10,22 +12,23 @@ describe('generateAppTypes', () => {
let config: d.ValidatedConfig;
let compilerCtx: d.CompilerCtx;
let buildCtx: d.BuildCtx;
let originalWriteFile: typeof compilerCtx.fs.writeFile;

const mockFs = {
writeFile: jest.fn(),
};
const mockWriteFile = jest.fn();

beforeEach(() => {
config = mockValidatedConfig({
srcDir: '/',
});
compilerCtx = {
...mockCompilerCtx(config),
fs: mockFs as any,
};
compilerCtx = mockCompilerCtx(config);
buildCtx = mockBuildCtx(config, compilerCtx);

mockFs.writeFile.mockResolvedValueOnce({ changedContent: true });
// Save the original write function to we can create a file in the
// in-memory fs if needed
originalWriteFile = compilerCtx.fs.writeFile;
compilerCtx.fs.writeFile = mockWriteFile;

mockWriteFile.mockResolvedValueOnce({ changedContent: true });
});

afterEach(() => {
Expand All @@ -42,7 +45,7 @@ describe('generateAppTypes', () => {

await generateAppTypes(config, compilerCtx, buildCtx, 'src');

expect(mockFs.writeFile).toHaveBeenCalledWith(
expect(mockWriteFile).toHaveBeenCalledWith(
'/components.d.ts',
`/* eslint-disable */
/* tslint:disable */
Expand Down Expand Up @@ -112,7 +115,7 @@ declare module "@stencil/core" {

await generateAppTypes(config, compilerCtx, buildCtx, 'src');

expect(mockFs.writeFile).toHaveBeenCalledWith(
expect(mockWriteFile).toHaveBeenCalledWith(
'/components.d.ts',
`/* eslint-disable */
/* tslint:disable */
Expand Down Expand Up @@ -204,7 +207,7 @@ declare module "@stencil/core" {

await generateAppTypes(config, compilerCtx, buildCtx, 'src');

expect(mockFs.writeFile).toHaveBeenCalledWith(
expect(mockWriteFile).toHaveBeenCalledWith(
'/components.d.ts',
`/* eslint-disable */
/* tslint:disable */
Expand Down Expand Up @@ -316,7 +319,7 @@ declare module "@stencil/core" {

await generateAppTypes(config, compilerCtx, buildCtx, 'src');

expect(mockFs.writeFile).toHaveBeenCalledWith(
expect(mockWriteFile).toHaveBeenCalledWith(
'/components.d.ts',
`/* eslint-disable */
/* tslint:disable */
Expand Down Expand Up @@ -460,7 +463,7 @@ declare module "@stencil/core" {

await generateAppTypes(config, compilerCtx, buildCtx, 'src');

expect(mockFs.writeFile).toHaveBeenCalledWith(
expect(mockWriteFile).toHaveBeenCalledWith(
'/components.d.ts',
`/* eslint-disable */
/* tslint:disable */
Expand Down Expand Up @@ -606,7 +609,7 @@ declare module "@stencil/core" {

await generateAppTypes(config, compilerCtx, buildCtx, 'src');

expect(mockFs.writeFile).toHaveBeenCalledWith(
expect(mockWriteFile).toHaveBeenCalledWith(
'/components.d.ts',
`/* eslint-disable */
/* tslint:disable */
Expand Down Expand Up @@ -730,7 +733,7 @@ declare module "@stencil/core" {

await generateAppTypes(config, compilerCtx, buildCtx, 'src');

expect(mockFs.writeFile).toHaveBeenCalledWith(
expect(mockWriteFile).toHaveBeenCalledWith(
'/components.d.ts',
`/* eslint-disable */
/* tslint:disable */
Expand Down Expand Up @@ -830,7 +833,7 @@ declare module "@stencil/core" {

await generateAppTypes(config, compilerCtx, buildCtx, 'src');

expect(mockFs.writeFile).toHaveBeenCalledWith(
expect(mockWriteFile).toHaveBeenCalledWith(
'/components.d.ts',
`/* eslint-disable */
/* tslint:disable */
Expand Down Expand Up @@ -942,7 +945,7 @@ declare module "@stencil/core" {

await generateAppTypes(config, compilerCtx, buildCtx, 'src');

expect(mockFs.writeFile).toHaveBeenCalledWith(
expect(mockWriteFile).toHaveBeenCalledWith(
'/components.d.ts',
`/* eslint-disable */
/* tslint:disable */
Expand Down Expand Up @@ -1082,7 +1085,7 @@ declare module "@stencil/core" {

await generateAppTypes(config, compilerCtx, buildCtx, 'src');

expect(mockFs.writeFile).toHaveBeenCalledWith(
expect(mockWriteFile).toHaveBeenCalledWith(
'/components.d.ts',
`/* eslint-disable */
/* tslint:disable */
Expand Down Expand Up @@ -1224,7 +1227,7 @@ declare module "@stencil/core" {

await generateAppTypes(config, compilerCtx, buildCtx, 'src');

expect(mockFs.writeFile).toHaveBeenCalledWith(
expect(mockWriteFile).toHaveBeenCalledWith(
'/components.d.ts',
`/* eslint-disable */
/* tslint:disable */
Expand Down Expand Up @@ -1346,7 +1349,7 @@ declare module "@stencil/core" {

await generateAppTypes(config, compilerCtx, buildCtx, 'src');

expect(mockFs.writeFile).toHaveBeenCalledWith(
expect(mockWriteFile).toHaveBeenCalledWith(
'/components.d.ts',
`/* eslint-disable */
/* tslint:disable */
Expand Down Expand Up @@ -1406,6 +1409,198 @@ declare module "@stencil/core" {
}
}
}
`,
{
immediateWrite: true,
}
);
});

it('should transform aliased paths if transformAliasedImportPaths is true', async () => {
const compilerComponentMeta = stubComponentCompilerMeta({
tagName: 'my-component',
componentClassName: 'MyComponent',
jsFilePath: path.join(config.rootDir, 'some/stubbed/path/a/my-component.js'),
sourceFilePath: path.join(config.rootDir, 'some/stubbed/path/a/my-component.tsx'),
sourceMapPath: path.join(config.rootDir, 'some/stubbed/path/a/my-component.js.map'),
hasProp: true,
properties: [
stubComponentCompilerProperty({
name: 'name',
complexType: {
original: 'UserImplementedPropType',
resolved: '"foo" | "bar"',
references: {
UserImplementedPropType: {
location: 'import',
path: '@utils',
},
},
},
}),
],
});
buildCtx.components = [compilerComponentMeta];
config.tsCompilerOptions = {
paths: {
'@utils': [path.join(config.rootDir, 'some/stubbed/path/utils/utils.ts')],
},
declaration: true,
};
config.transformAliasedImportPaths = true;
// We need to have a file in the in-memory fs for the TS module resolution to succeed
await originalWriteFile(path.join(config.rootDir, 'some/stubbed/path/utils/utils.ts'), '');

patchTypescript(config, compilerCtx.fs);

await generateAppTypes(config, compilerCtx, buildCtx, 'src');

expect(mockWriteFile).toHaveBeenCalledWith(
'/components.d.ts',
`/* eslint-disable */
/* tslint:disable */
/**
* This is an autogenerated file created by the Stencil compiler.
* It contains typing information for all components that exist in this project.
*/
import { HTMLStencilElement, JSXBase } from "@stencil/core/internal";
import { UserImplementedPropType } from "./some/stubbed/path/utils/utils";
export { UserImplementedPropType } from "./some/stubbed/path/utils/utils";
export namespace Components {
/**
* docs
*/
interface MyComponent {
"name": UserImplementedPropType;
}
}
declare global {
/**
* docs
*/
interface HTMLMyComponentElement extends Components.MyComponent, HTMLStencilElement {
}
var HTMLMyComponentElement: {
prototype: HTMLMyComponentElement;
new (): HTMLMyComponentElement;
};
interface HTMLElementTagNameMap {
"my-component": HTMLMyComponentElement;
}
}
declare namespace LocalJSX {
/**
* docs
*/
interface MyComponent {
"name"?: UserImplementedPropType;
}
interface IntrinsicElements {
"my-component": MyComponent;
}
}
export { LocalJSX as JSX };
declare module "@stencil/core" {
export namespace JSX {
interface IntrinsicElements {
/**
* docs
*/
"my-component": LocalJSX.MyComponent & JSXBase.HTMLAttributes<HTMLMyComponentElement>;
}
}
}
`,
{
immediateWrite: true,
}
);
});

it('should not transform aliased paths if transformAliasedImportPaths is false', async () => {
const compilerComponentMeta = stubComponentCompilerMeta({
tagName: 'my-component',
componentClassName: 'MyComponent',
jsFilePath: '/some/stubbed/path/a/my-component.js',
sourceFilePath: '/some/stubbed/path/a/my-component.tsx',
sourceMapPath: '/some/stubbed/path/a/my-component.js.map',
hasProp: true,
properties: [
stubComponentCompilerProperty({
name: 'name',
complexType: {
original: 'UserImplementedPropType',
resolved: '"foo" | "bar"',
references: {
UserImplementedPropType: {
location: 'import',
path: '@utils',
},
},
},
}),
],
});
buildCtx.components = [compilerComponentMeta];
config.tsCompilerOptions = {};

await generateAppTypes(config, compilerCtx, buildCtx, 'src');

expect(mockWriteFile).toHaveBeenCalledWith(
'/components.d.ts',
`/* eslint-disable */
/* tslint:disable */
/**
* This is an autogenerated file created by the Stencil compiler.
* It contains typing information for all components that exist in this project.
*/
import { HTMLStencilElement, JSXBase } from "@stencil/core/internal";
import { UserImplementedPropType } from "@utils";
export { UserImplementedPropType } from "@utils";
export namespace Components {
/**
* docs
*/
interface MyComponent {
"name": UserImplementedPropType;
}
}
declare global {
/**
* docs
*/
interface HTMLMyComponentElement extends Components.MyComponent, HTMLStencilElement {
}
var HTMLMyComponentElement: {
prototype: HTMLMyComponentElement;
new (): HTMLMyComponentElement;
};
interface HTMLElementTagNameMap {
"my-component": HTMLMyComponentElement;
}
}
declare namespace LocalJSX {
/**
* docs
*/
interface MyComponent {
"name"?: UserImplementedPropType;
}
interface IntrinsicElements {
"my-component": MyComponent;
}
}
export { LocalJSX as JSX };
declare module "@stencil/core" {
export namespace JSX {
interface IntrinsicElements {
/**
* docs
*/
"my-component": LocalJSX.MyComponent & JSXBase.HTMLAttributes<HTMLMyComponentElement>;
}
}
}
`,
{
immediateWrite: true,
Expand Down
Loading

0 comments on commit fd63c17

Please sign in to comment.