Skip to content

Commit

Permalink
Support import paths with .gts extensions
Browse files Browse the repository at this point in the history
Fixes #618
  • Loading branch information
lukemelia committed Sep 20, 2023
1 parent 5861a8b commit 7e6ec8d
Show file tree
Hide file tree
Showing 17 changed files with 584 additions and 306 deletions.
8 changes: 4 additions & 4 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ jobs:
- name: Install Node
uses: actions/setup-node@v3
with:
node-version: 14
node-version: 16
- name: Locate Yarn Cache
id: yarn-cache-dir-path
run: echo "dir=$(yarn cache dir)" >> $GITHUB_OUTPUT
Expand Down Expand Up @@ -60,7 +60,7 @@ jobs:
- name: Install Node
uses: actions/setup-node@v3
with:
node-version: 14
node-version: 16
- name: Install Dependencies
uses: nick-fields/retry@v2
with:
Expand Down Expand Up @@ -93,7 +93,7 @@ jobs:
- name: Install Node
uses: actions/setup-node@v3
with:
node-version: 14
node-version: 16
- name: Install Dependencies
run: yarn install --no-lockfile
- name: Build
Expand All @@ -112,7 +112,7 @@ jobs:
- name: Install Node
uses: actions/setup-node@v3
with:
node-version: 14
node-version: 16
- name: Locate Yarn Cache
id: yarn-cache-dir-path
run: echo "dir=$(yarn cache dir)" >> $GITHUB_OUTPUT
Expand Down
348 changes: 174 additions & 174 deletions packages/core/__tests__/transform/debug.test.ts

Large diffs are not rendered by default.

244 changes: 134 additions & 110 deletions packages/core/__tests__/transform/rewrite.test.ts

Large diffs are not rendered by default.

9 changes: 8 additions & 1 deletion packages/core/src/common/document-cache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,15 @@ export default class DocumentCache {
);
}

private gtsAwareExtName(filename: string): string {
if (filename.endsWith('.gts.ts')) {
return '.gts.ts';
}
return path.extname(filename);
}

public getCandidateDocumentPaths(filename: string): Array<string> {
let extension = path.extname(filename);
let extension = this.gtsAwareExtName(filename);
let filenameWithoutExtension = filename.slice(0, filename.lastIndexOf(extension));

return this.getCandidateExtensions(filename).map((ext) => `${filenameWithoutExtension}${ext}`);
Expand Down
6 changes: 5 additions & 1 deletion packages/core/src/config/types.cts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export type GlintExtensionPreprocess<T> = (
export type GlintEmitMetadata = {
prepend?: string;
append?: string;
replaceExtension?: string;
templateLocation?: {
start: number;
end: number;
Expand All @@ -37,7 +38,10 @@ export type GlintExtensionTransform<T> = (
state: {
ts: TSLib;
context: ts.TransformationContext;
setEmitMetadata: (node: ts.TaggedTemplateExpression, meta: GlintEmitMetadata) => void;
setEmitMetadata: (
node: ts.TaggedTemplateExpression | ts.ImportDeclaration,
meta: GlintEmitMetadata
) => void;
}
) => ts.Transformer<ts.Node>;

Expand Down
54 changes: 54 additions & 0 deletions packages/core/src/transform/template/import-declaration-span.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import * as ts from 'typescript';
import { TSLib } from '../util.js';
import { CorrelatedSpansResult, PartialCorrelatedSpan } from './inlining/index.js';
import { Directive, SourceFile, TransformError } from './transformed-module.js';
import { GlintEmitMetadata } from '@glint/core/config-types';
import MappingTree, { GtsImport } from './mapping-tree.js';

const tsPrinter = ts.createPrinter();

export function calculateImportSpan(
ts: TSLib,
node: ts.ImportDeclaration,
meta: GlintEmitMetadata | undefined,
script: SourceFile
): CorrelatedSpansResult {
let directives: Array<Directive> = [];
let errors: Array<TransformError> = [];
let partialSpans: Array<PartialCorrelatedSpan> = [];
let moduleSpecifierText = (node.moduleSpecifier as ts.StringLiteral).text;
if (meta?.replaceExtension) {
let newModuleSpecifier = ts.factory.createStringLiteral(
moduleSpecifierText.replace(/\.gts$/, '.' + meta.replaceExtension)
);
let updatedNode = ts.factory.updateImportDeclaration(
node,
node.modifiers,
node.importClause,
newModuleSpecifier,
node.assertClause
);
let originalStart = node.getStart();
let originalLength = node.getEnd() - originalStart;
let transformedSource = tsPrinter.printNode(
ts.EmitHint.Unspecified,
updatedNode,
node.getSourceFile()
);
partialSpans.push({
originalFile: script,
originalStart,
originalLength,
insertionPoint: originalStart,
transformedSource,
mapping: new MappingTree(
{ start: 0, end: transformedSource.length },
{ start: 0, end: originalLength },
[],
new GtsImport()
),
});
}

return { errors, directives, partialSpans };
}
29 changes: 21 additions & 8 deletions packages/core/src/transform/template/mapping-tree.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,13 @@ import { AST } from '@glimmer/syntax';
import { Range } from './transformed-module.js';
import { Identifier } from './map-template-contents.js';

export type MappingSource = AST.Node | TemplateEmbedding | TextContent | Identifier | ParseError;
export type MappingSource =
| AST.Node
| TemplateEmbedding
| TextContent
| Identifier
| GtsImport
| ParseError;

/**
* In cases where we're unable to parse a template, we still want to
Expand Down Expand Up @@ -34,6 +40,13 @@ export class TemplateEmbedding {
public readonly type = 'TemplateEmbedding';
}

/**
* This node represents an import declaration of a .gts file.
*/
export class GtsImport {
public readonly type = 'GtsImport';
}

/**
* A `MappingTree` maintains a hierarchy of mappings between ranges of
* locations in original and transformed source strings. These mappings
Expand Down Expand Up @@ -109,23 +122,23 @@ export default class MappingTree {
}): string {
let { originalSource, transformedSource, indent = '| ' } = options;
let { sourceNode, originalRange, transformedRange, children } = this;
let hbsStart = options.originalStart + originalRange.start;
let hbsEnd = options.originalStart + originalRange.end;
let sourceStart = options.originalStart + originalRange.start;
let sourceEnd = options.originalStart + originalRange.end;
let tsStart = options.transformedStart + transformedRange.start;
let tsEnd = options.transformedStart + transformedRange.end;
let lines = [];

lines.push(`${indent}Mapping: ${sourceNode.type}`);

let sourceDesc = sourceNode.type === 'GtsImport' ? 'ts' : 'hbs';
lines.push(
`${indent}${` hbs(${hbsStart}:${hbsEnd}):`.padEnd(15)}${this.getSourceRange(
originalSource,
originalRange
)}`
`${indent}${` in: ${sourceDesc}(${sourceStart}:${sourceEnd}):`.padEnd(
20
)}${this.getSourceRange(originalSource, originalRange)}`
);

lines.push(
`${indent}${` ts(${tsStart}:${tsEnd}):`.padEnd(15)}${this.getSourceRange(
`${indent}${` out: ts(${tsStart}:${tsEnd}):`.padEnd(20)}${this.getSourceRange(
transformedSource,
transformedRange
)}`
Expand Down
8 changes: 7 additions & 1 deletion packages/core/src/transform/template/rewrite-module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import TransformedModule, {
} from './transformed-module.js';
import { calculateTaggedTemplateSpans } from './inlining/tagged-strings.js';
import { calculateCompanionTemplateSpans } from './inlining/companion-file.js';
import { calculateImportSpan } from './import-declaration-span.js';

/**
* Input to the process of rewriting a template, containing one or both of:
Expand Down Expand Up @@ -86,8 +87,13 @@ function calculateCorrelatedSpans(
} else if (ts.isModuleDeclaration(node)) {
// don't traverse into declare module
return node;
} else if (ts.isImportDeclaration(node) && ts.isStringLiteral(node.moduleSpecifier)) {
let meta = emitMetadata.get(node);
let result = calculateImportSpan(ts, node, meta, script);
directives.push(...result.directives);
errors.push(...result.errors);
partialSpans.push(...result.partialSpans);
}

return ts.visitEachChild(node, visit, context);
},
]);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,13 @@ export const transform: GlintExtensionTransform<PreprocessData> = (
if (ts.isSourceFile(node)) {
// Add `import { hbs as __T } from 'ember-template-imports'` to the file
return addTagImport(f, node);
} else if (ts.isImportDeclaration(node) && ts.isStringLiteral(node.moduleSpecifier)) {
if (node.moduleSpecifier.text.endsWith('.gts')) {
setEmitMetadata(node, {
replaceExtension: 'ts',
});
}
return node;
} else if (isETIDefaultTemplate(ts, node)) {
// Annotate that this template is a default export
setEmitMetadata(node.expression, { prepend: 'export default ' });
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import Component from '@glimmer/component';
import { TOC } from '@ember/component/template-only';
import repeat from '../helpers/repeat';

export interface GreetingSignature {
Args: { target: string };
Expand All @@ -13,10 +14,12 @@ export default class Greeting extends Component<GreetingSignature> {
</template>
}

function repeat(value: string, times: number): string {
return Array(times).fill(value).join('');
}

const Bang: TOC<{ Args: { times: number } }> = <template>
{{repeat '!' @times}}
</template>

declare module '@glint/environment-ember-loose/registry' {
export default interface Registry {
Greeting: typeof Greeting;
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import Greeting from './Greeting';
import Greeting from './Greeting.gts';

export default <template>
<Greeting @target="World" />
Expand Down
4 changes: 4 additions & 0 deletions test-packages/ts-template-imports-app/app/helpers/repeat.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export default function repeat(value: string, times: number): string {
return value.repeat(times);
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<div class="main">
<Greeting @target="Earthling" />
</div>
12 changes: 11 additions & 1 deletion test-packages/ts-template-imports-app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,17 @@
"private": true,
"license": "MIT",
"scripts": {
"test": "glint"
"test": "npm-run-all test:*",
"test:typecheck": "glint",
"test:tsc": "tsc --noEmit"
},
"devDependencies": {
"@typescript-eslint/eslint-plugin": "~6.7.2",
"@typescript-eslint/parser": "~6.7.2",
"typescript": "~5.2.0"
},
"engines": {
"node": ">= 16"
},
"volta": {
"extends": "../../package.json"
Expand Down
6 changes: 5 additions & 1 deletion test-packages/ts-template-imports-app/tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
{
"extends": "../../tsconfig.compileroptions.json",
"include": ["src", "types"],
"compilerOptions": {
"allowImportingTsExtensions": true,
"noEmit": true
},
"include": ["app", "types"],
"glint": {
"environment": {
"ember-loose": {},
Expand Down
Loading

0 comments on commit 7e6ec8d

Please sign in to comment.