Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
130 changes: 61 additions & 69 deletions packages/core/__tests__/cli/build-watch.test.ts

Large diffs are not rendered by default.

329 changes: 152 additions & 177 deletions packages/core/__tests__/cli/build.test.ts

Large diffs are not rendered by default.

26 changes: 11 additions & 15 deletions packages/core/__tests__/cli/incremental.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,15 @@ import { beforeEach, describe, expect, test } from 'vitest';
import { Project } from '@glint/test-utils';

const BUILD_INFO = 'tsconfig.tsbuildinfo';
const INPUT_SCRIPT = 'index.ts';
const INPUT_SCRIPT = 'index.gts';

describe('CLI: --incremental', () => {
test('when no build has occurred', async () => {
let project = await Project.create();
let project = await Project.create({ glint: { environment: 'ember-template-imports' } });

let code = stripIndent`
import '@glint/environment-ember-template-imports';
import Component from '@glimmer/component';
import { hbs } from 'ember-template-imports';

type ApplicationArgs = {
version: string;
Expand All @@ -24,10 +23,10 @@ describe('CLI: --incremental', () => {
export default class Application extends Component<{ Args: ApplicationArgs }> {
private startupTime = new Date().toISOString();

public static template = hbs\`
<template>
Welcome to app v{{@version}}.
The current time is {{this.startupTime}}.
\`;
</template>
}
`;

Expand All @@ -49,12 +48,11 @@ describe('CLI: --incremental', () => {
describe('when a build has occurred', () => {
let project!: Project;
beforeEach(async () => {
project = await Project.create();
project = await Project.create({ glint: { environment: 'ember-template-imports' } });

let code = stripIndent`
import '@glint/environment-ember-template-imports';
import Component from '@glimmer/component';
import { hbs } from 'ember-template-imports';

type ApplicationArgs = {
version: string;
Expand All @@ -63,10 +61,10 @@ describe('CLI: --incremental', () => {
export default class Application extends Component<{ Args: ApplicationArgs }> {
private startupTime = new Date().toISOString();

public static template = hbs\`
<template>
Welcome to app v{{@version}}.
The current time is {{this.startupTime}}.
\`;
</template>
}
`;

Expand Down Expand Up @@ -96,7 +94,6 @@ describe('CLI: --incremental', () => {
let code = stripIndent`
import '@glint/environment-ember-template-imports';
import Component from '@glimmer/component';
import { hbs } from 'ember-template-imports';

type ApplicationArgs = {
appVersion: string;
Expand All @@ -105,10 +102,10 @@ describe('CLI: --incremental', () => {
export default class Application extends Component<{ Args: ApplicationArgs }> {
private startupTime = new Date().toISOString();

public static template = hbs\`
<template>
Welcome to app v{{@appVersion}}.
The current time is {{this.startupTime}}.
\`;
</template>
}
`;

Expand All @@ -135,7 +132,6 @@ describe('CLI: --incremental', () => {
let code = stripIndent`
import '@glint/environment-ember-template-imports';
import Component from '@glimmer/component';
import { hbs } from 'ember-template-imports';

type ApplicationArgs = {
version: string;
Expand All @@ -144,10 +140,10 @@ describe('CLI: --incremental', () => {
export default class Application extends Component<{ Args: ApplicationArgs }> {
private startupTime = new Date().toISOString();

public static template = hbs\`
<template>
Welcome to app v{{@version}}.
The current time is {{this.startupTime}}.
\`;
</template.
}
`;

Expand Down
8 changes: 4 additions & 4 deletions packages/core/__tests__/transform/debug.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,7 @@ describe('Transform: Debug utilities', () => {
|
| | Mapping: Template
| | hbs(155:200): <HelperComponent @foo={{this.bar}} />
| | ts(324:451): hbs;\\\\n {\\\\n const 𝛄 = χ.emitComponent(χ.resolve(HelperComponent)({ foo: 𝚪.this.bar, ...χ.NamedArgsMarker }));\\\\n 𝛄;\\\\n }
| | ts(331:451): {\\\\n const 𝛄 = χ.emitComponent(χ.resolve(HelperComponent)({ foo: 𝚪.this.bar, ...χ.NamedArgsMarker }));\\\\n 𝛄;\\\\n }
| |
| | | Mapping: TextContent
| | | hbs(155:160):
Expand Down Expand Up @@ -260,7 +260,7 @@ describe('Transform: Debug utilities', () => {
|
| | Mapping: Template
| | hbs(299:418): <p ...attributes>\\\\n Hello, {{@foo}}!\\\\n\\\\n {{! @glint-expect-error: no @bar arg }}\\\\n {{@bar}}\\\\n </p>
| | ts(729:935): hbs;\\\\n {\\\\n const 𝛄 = χ.emitElement(\\"p\\");\\\\n χ.applySplattributes(𝚪.element, 𝛄.element);\\\\n χ.emitContent(χ.resolveOrReturn(𝚪.args.foo)());\\\\n χ.emitContent(χ.resolveOrReturn(𝚪.args.bar)());\\\\n }
| | ts(736:935): {\\\\n const 𝛄 = χ.emitElement(\\"p\\");\\\\n χ.applySplattributes(𝚪.element, 𝛄.element);\\\\n χ.emitContent(χ.resolveOrReturn(𝚪.args.foo)());\\\\n χ.emitContent(χ.resolveOrReturn(𝚪.args.bar)());\\\\n }
| |
| | | Mapping: TextContent
| | | hbs(299:304):
Expand Down Expand Up @@ -370,7 +370,7 @@ describe('Transform: Debug utilities', () => {
|
| | Mapping: Template
| | hbs(160:207): <HelperComponent @foo={{this.bar}} />
| | ts(329:456): hbs;\\\\n {\\\\n const 𝛄 = χ.emitComponent(χ.resolve(HelperComponent)({ foo: 𝚪.this.bar, ...χ.NamedArgsMarker }));\\\\n 𝛄;\\\\n }
| | ts(336:456): {\\\\n const 𝛄 = χ.emitComponent(χ.resolve(HelperComponent)({ foo: 𝚪.this.bar, ...χ.NamedArgsMarker }));\\\\n 𝛄;\\\\n }
| |
| | | Mapping: TextContent
| | | hbs(160:166):
Expand Down Expand Up @@ -425,7 +425,7 @@ describe('Transform: Debug utilities', () => {
|
| | Mapping: Template
| | hbs(310:436): <p ...attributes>\\\\r\\\\n Hello, {{@foo}}!\\\\r\\\\n\\\\r\\\\n {{! @glint-expect-error: no @bar arg }}\\\\r\\\\n {{@bar}}\\\\r\\\\n </p>
| | ts(738:944): hbs;\\\\n {\\\\n const 𝛄 = χ.emitElement(\\"p\\");\\\\n χ.applySplattributes(𝚪.element, 𝛄.element);\\\\n χ.emitContent(χ.resolveOrReturn(𝚪.args.foo)());\\\\n χ.emitContent(χ.resolveOrReturn(𝚪.args.bar)());\\\\n }
| | ts(745:944): {\\\\n const 𝛄 = χ.emitElement(\\"p\\");\\\\n χ.applySplattributes(𝚪.element, 𝛄.element);\\\\n χ.emitContent(χ.resolveOrReturn(𝚪.args.foo)());\\\\n χ.emitContent(χ.resolveOrReturn(𝚪.args.bar)());\\\\n }
| |
| | | Mapping: TextContent
| | | hbs(310:316):
Expand Down
29 changes: 18 additions & 11 deletions packages/core/__tests__/transform/rewrite.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,10 +98,15 @@ describe('Transform: rewriteModule', () => {
let transformedModule = rewriteModule(ts, { script }, glimmerxEnvironment);

expect(transformedModule?.errors.length).toBe(1);
expect(transformedModule?.transformedContents).toBe(script.contents);

expect(transformedModule?.getOriginalOffset(100)).toEqual({ offset: 100, source: script });
expect(transformedModule?.getTransformedOffset(script.filename, 100)).toEqual(100);
expect(transformedModule?.transformedContents).toMatchInlineSnapshot(`
"import Component, { hbs } from '@glimmerx/component';
export default class MyComponent extends Component {
static template = ({} as typeof import(\\"@glint/environment-glimmerx/-private/dsl\\")).templateForBackingValue(this, function(𝚪, χ: typeof import(\\"@glint/environment-glimmerx/-private/dsl\\")) {
hbs;
𝚪; χ;
});
}"
`);
});

test('outer variable capture', () => {
Expand Down Expand Up @@ -449,13 +454,15 @@ describe('Transform: rewriteModule', () => {
let transformedModule = rewriteModule(ts, { script, template }, emberLooseEnvironment);

expect(transformedModule?.errors.length).toBe(1);
expect(transformedModule?.transformedContents).toBe(script.contents);

expect(transformedModule?.getOriginalOffset(50)).toEqual({ offset: 50, source: script });
expect(transformedModule?.getTransformedOffset(script.filename, 50)).toEqual(50);
expect(transformedModule?.getTransformedOffset(template.filename, 5)).toEqual(
script.contents.lastIndexOf('}')
);
expect(transformedModule?.transformedContents).toMatchInlineSnapshot(`
"import Component from '@glimmer/component';
export default class MyComponent extends Component {
static {
({} as typeof import(\\"@glint/environment-ember-loose/-private/dsl\\")).templateForBackingValue(this, function(𝚪, χ: typeof import(\\"@glint/environment-ember-loose/-private/dsl\\")) {
𝚪; χ;
})}
}"
`);
});
});

Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/cli/utils/patch-program.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export function patchProgram(
if (isPool(transformManagerOrPool)) {
let configFile = program.getCompilerOptions()['configFilePath'];
assert(typeof configFile === 'string', 'internal error: missing TS config file');
manager = transformManagerOrPool.managerFor(configFile);
manager = transformManagerOrPool.managerForFile(configFile);
} else {
manager = transformManagerOrPool;
}
Expand Down
23 changes: 15 additions & 8 deletions packages/core/src/cli/utils/transform-manager-pool.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { dirname } from 'node:path';
import * as TS from 'typescript';
import { ConfigLoader, GlintConfig } from '../../config/index.js';
import TransformManager from '../../common/transform-manager.js';
Expand Down Expand Up @@ -28,8 +29,12 @@ export default class TransformManagerPool {
this.#rootSys = sys;
}

public managerFor(path: string): TransformManager | null {
let config = this.#loader.configForFile(path);
public managerForFile(path: string): TransformManager | null {
return this.managerForDirectory(dirname(path));
}

public managerForDirectory(path: string): TransformManager | null {
let config = this.#loader.configForDirectory(path);
if (!config) return null;

const existing = this.#managers.get(config);
Expand All @@ -47,7 +52,8 @@ export default class TransformManagerPool {
includes: ReadonlyArray<string>,
depth?: number | undefined
): Array<string> => {
let readDirectory = this.managerFor(rootDir)?.readDirectory ?? this.#rootSys.readDirectory;
let readDirectory =
this.managerForDirectory(rootDir)?.readDirectory ?? this.#rootSys.readDirectory;
return readDirectory(rootDir, extensions, excludes, includes, depth);
};

Expand All @@ -58,12 +64,13 @@ export default class TransformManagerPool {
options?: TS.WatchOptions
): TS.FileWatcher => {
assert(this.#rootSys.watchDirectory);
let watchDirectory = this.managerFor(path)?.watchDirectory ?? this.#rootSys.watchDirectory;
let watchDirectory =
this.managerForDirectory(path)?.watchDirectory ?? this.#rootSys.watchDirectory;
return watchDirectory(path, originalCallback, recursive, options);
};

public fileExists = (filename: string): boolean => {
let fileExists = this.managerFor(filename)?.fileExists ?? this.#rootSys.fileExists;
let fileExists = this.managerForFile(filename)?.fileExists ?? this.#rootSys.fileExists;
return fileExists(filename);
};

Expand All @@ -75,20 +82,20 @@ export default class TransformManagerPool {
): TS.FileWatcher => {
assert(this.#rootSys.watchFile);
let watchTransformedFile =
this.managerFor(path)?.watchTransformedFile ?? this.#rootSys.watchFile;
this.managerForFile(path)?.watchTransformedFile ?? this.#rootSys.watchFile;
return watchTransformedFile(path, originalCallback, pollingInterval, options);
};

public readTransformedFile = (filename: string, encoding?: string): string | undefined => {
let readTransformedFile =
this.managerFor(filename)?.readTransformedFile ?? this.#rootSys.readFile;
this.managerForFile(filename)?.readTransformedFile ?? this.#rootSys.readFile;
return readTransformedFile(filename, encoding);
};

public getModifiedTime = (filename: string): Date | undefined => {
assert(this.#rootSys.getModifiedTime);
let getModifiedTime =
this.managerFor(filename)?.getModifiedTime ?? this.#rootSys.getModifiedTime;
this.managerForFile(filename)?.getModifiedTime ?? this.#rootSys.getModifiedTime;
return getModifiedTime(filename);
};
}
15 changes: 12 additions & 3 deletions packages/core/src/common/transform-manager.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { statSync as fsStatSync, Stats } from 'fs';
import { statSync as fsStatSync, Stats, existsSync } from 'fs';
import {
TransformedModule,
rewriteModule,
Expand Down Expand Up @@ -241,10 +241,19 @@ export default class TransformManager {
};

public getModifiedTime = (filename: string): Date | undefined => {
let fileStat = statSync(filename);
// In most circumstances we can just ask the DocumentCache what the canonical path
// for a given document is, but since `getModifiedTime` is invoked as part of
// rehydrating a `.tsbuildinfo` file, typically won't actually know the answer to
// that question yet.
let canonicalFilename = this.documents
.getCandidateDocumentPaths(filename)
.find((path) => existsSync(path));
if (!canonicalFilename) return undefined;

let fileStat = statSync(canonicalFilename);
if (!fileStat) return undefined;

let companionPath = this.documents.getCompanionDocumentPath(filename);
let companionPath = this.documents.getCompanionDocumentPath(canonicalFilename);
if (!companionPath) return fileStat.mtime;

let companionStat = statSync(companionPath);
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/language-server/glint-language-server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,7 @@ export default class GlintLanguageServer {
// we're in a template file, then we have no completions to offer.
if (
mapping?.sourceNode.type === 'TextContent' ||
mapping?.sourceNode.type === 'TemplateEmbedding' ||
(!mapping && this.glintConfig.environment.isTemplate(uri))
) {
return;
Expand Down
10 changes: 4 additions & 6 deletions packages/core/src/transform/template/map-template-contents.ts
Original file line number Diff line number Diff line change
Expand Up @@ -129,9 +129,10 @@ export type MapTemplateContentsOptions = {
export function mapTemplateContents(
template: string,
{ embeddingSyntax }: MapTemplateContentsOptions,
callback: (ast: AST.Template, mapper: Mapper) => void
callback: (ast: AST.Template | null, mapper: Mapper) => void
): RewriteResult {
let ast: AST.Template;
let ast: AST.Template | null = null;
let errors: Array<{ message: string; location: Range | undefined }> = [];
let lineOffsets = calculateLineOffsets(template, embeddingSyntax.prefix.length);
try {
ast = preprocess(template);
Expand All @@ -151,9 +152,7 @@ export function mapTemplateContents(
}
}

return {
errors: [{ message, location }],
};
errors.push({ message, location });
}

let rangeForNode = buildRangeForNode(lineOffsets);
Expand All @@ -167,7 +166,6 @@ export function mapTemplateContents(
let indent = '';
let offset = 0;
let needsIndent = false;
let errors: Array<{ message: string; location: Range }> = [];
let directives: Array<LocalDirective> = [];

// Associates all content emitted during the given callback with the
Expand Down
16 changes: 9 additions & 7 deletions packages/core/src/transform/template/template-to-typescript.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,12 +44,7 @@ export function templateToTypescript(
let scope = new ScopeStack([]);

emitTemplateBoilerplate(() => {
for (let line of preamble) {
emit.text(line);
emit.newline();
}

for (let statement of ast.body) {
for (let statement of ast?.body ?? []) {
emitTopLevelStatement(statement);
}
});
Expand Down Expand Up @@ -109,7 +104,14 @@ export function templateToTypescript(
emit.newline();
emit.indent();

emit.forNode(ast, emitBody);
for (let line of preamble) {
emit.text(line);
emit.newline();
}

if (ast) {
emit.forNode(ast, emitBody);
}

// Ensure the context and lib variables are always consumed to prevent
// an unused variable warning
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,3 @@ type EELLinkTo = typeof Globals.LinkTo;
declare module '@ember/routing' {
export interface LinkTo extends EELLinkTo {}
}

import 'ember-template-imports';
declare module 'ember-template-imports' {
export function hbs(contents: TemplateStringsArray): unknown;
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export default function emberTemplateImportsEnvironment(

return {
tags: {
'ember-template-imports': {
'@glint/environment-ember-template-imports/-private/tag': {
hbs: {
typesModule: '@glint/environment-ember-template-imports/-private/dsl',
specialForms: {
Expand Down
Loading