Skip to content

Use content tag #615

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 4 commits into from
Closed
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
13 changes: 13 additions & 0 deletions packages/core/src/common/transform-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,11 @@ export default class TransformManager {
): ReadonlyArray<ts.Diagnostic> {
let unusedExpectErrors = new Set(this.getExpectErrorDirectives(fileName));
let allDiagnostics = [];

for (let diagnostic of diagnostics) {
if (diagnostic.messageText === `Cannot find name 'template'`) {
continue;
}
let { rewrittenDiagnostic, appliedDirective } = this.rewriteDiagnostic(diagnostic);
if (rewrittenDiagnostic) {
allDiagnostics.push(rewrittenDiagnostic);
Expand All @@ -85,6 +89,15 @@ export default class TransformManager {
);
}

// When we have syntax errors we get _too many errors_
// if we have an issue with <template> tranformation, we should
// make the user fix their syntax before revealing all the other errors.
let glint = allDiagnostics.filter((diagnostic) => 'isGlintTransformDiagnostic' in diagnostic);
if (glint.length) {
return this.ts.sortAndDeduplicateDiagnostics(glint);
}


return this.ts.sortAndDeduplicateDiagnostics(allDiagnostics);
}

Expand Down
3 changes: 3 additions & 0 deletions packages/core/src/language-server/glint-language-server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ export default class GlintLanguageServer {
private documents: DocumentCache,
private transformManager: TransformManager
) {
console.log('Glint starting...');

let parsedConfig = this.parseTsconfig(glintConfig, transformManager);

this.ts = glintConfig.ts;
Expand Down Expand Up @@ -94,6 +96,7 @@ export default class GlintLanguageServer {

// Kickstart typechecking
this.service.getProgram();
console.log('Glint started');
}

public dispose(): void {
Expand Down
95 changes: 92 additions & 3 deletions packages/core/src/transform/template/rewrite-module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,32 @@ function calculateCorrelatedSpans(
let directives: Array<Directive> = [];
let errors: Array<TransformError> = [];
let partialSpans: Array<PartialCorrelatedSpan> = [];
let { ast, emitMetadata, error } = parseScript(ts, script, environment);

if (error) {
if (typeof error === 'string') {
errors.push({ message: error, location: { start: 0, end: script.contents.length - 1 }, source: script });
} else if ('raw' in error) {
// these lines exclude the line with the error, because
// adding the column offset will get us on to the line with the error
let lines = script.contents.split('\n').slice(0, error.line);
let start = lines.reduce((sum, line) => sum + line.length, 0) + error.column - 1;
let end = start + 1;

errors.push({
message: error.message + '\n' + error.help,
source: script,
location: {
start,
end,
}
})
}

let { ast, emitMetadata } = parseScript(ts, script, environment);
// We've hit a parsing error, so we need to immediately return as the parsed
// document must be correct before we can continue.
return { errors, directives, partialSpans };
}

ts.transform(ast, [
(context) =>
Expand Down Expand Up @@ -103,9 +127,19 @@ function calculateCorrelatedSpans(
return { errors, directives, partialSpans };
}

type ParseError = string | {
message: string;
line: number;
column: number;
file: string;
help: string;
raw: string;
};

type ParseResult = {
ast: ts.SourceFile;
emitMetadata: WeakMap<ts.Node, GlintEmitMetadata>;
error?: ParseError;
};

function parseScript(ts: TSLib, script: SourceFile, environment: GlintEnvironment): ParseResult {
Expand All @@ -116,7 +150,62 @@ function parseScript(ts: TSLib, script: SourceFile, environment: GlintEnvironmen
void emitMetadata.set(node, Object.assign(emitMetadata.get(node) ?? {}, data));

let { preprocess, transform } = environment.getConfigForExtension(extension) ?? {};
let preprocessed = preprocess?.(contents, filename) ?? { contents };
let original: {
contents: string;
data?: {
// SAFETY: type exists elsewhere (the environments)
templateLocations: any[];
};
} = { contents, data: { templateLocations: [] } };
let preprocessed = original;
let error: ParseError | undefined;

try {
preprocessed = preprocess?.(contents, filename) ?? original;
} catch (e) {
if (typeof e === 'object' && e !== null) {
// Parse Errors from the rust parser
if ('source_code' in e) {
// We remove the blank links in the error because swc
// pads errors with a leading and trailing blank line.
// the error is typically meant for the terminal, so making it
// stand out a bit more is a good, but that's more a presentation
// concern than just pure error information (which is what we need).
// @ts-expect-error object / property narrowing isn't available until TS 5.1
let lines = e.source_code.split('\n').filter(Boolean);
// Example:
// ' × Unexpected eof'
// ' ╭─[/home/nullvoxpopuli/Development/OpenSource/glint/test-packages/ts-template-imports-app/src/index.gts:6:1]'
// ' 6 │ '
// ' 7 │ export const X = <tem'
// ' ╰────'
let raw = lines.join('\n');
let message = lines[0].replace('×', '').trim();
let info = lines[1];
// a filename may have numbers in it, so we want to remove the filename
// before regex searching for numbers at the end of this line
let strippedInfo = info.replace(filename, '');
let matches = [...strippedInfo.matchAll(/\d+/g)];
let line = parseInt(matches[0][0], 10);
let column = parseInt(matches[1][0], 10);
let help = lines.slice(1).join('\n');

error = {
raw,
message,
line,
column,
file: filename,
help,
}
} else {
error = `${e}`;
}
} else {
error = `${e}`;
}
}

let ast = ts.createSourceFile(
filename,
preprocessed.contents,
Expand All @@ -133,7 +222,7 @@ function parseScript(ts: TSLib, script: SourceFile, environment: GlintEnvironmen
ast = transformed[0];
}

return { ast, emitMetadata };
return { ast, emitMetadata, error };
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -1,26 +1,51 @@
import { GlintExtensionPreprocess } from '@glint/core/config-types';
import { parseTemplates } from 'ember-template-imports';
import { GLOBAL_TAG, PreprocessData, TemplateLocation } from './common';

/**
* We don't need to actually generate the code that would be emitted for
* a real app to build, we can use placeholders, so we have less offsets
* to worry about (precompile, setComponentTemplate, their imports, etc)
*/
const TEMPLATE_START = `[${GLOBAL_TAG}\``;
const TEMPLATE_END = '`]';

import { Preprocessor } from 'content-tag';
const p = new Preprocessor();

interface Parsed {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we have content-tag expose this type itself, either by generating it from the underlying Rust struct(s) or something like #[wasm_bindgen(typescript_custom_section)]?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think so -- tho I don't know how specific we'd get. @ef4 said an update to one of the build tools would give us better types when updated.

It's also totally reasonable for us to ship our own d.ts, and ignore the generated one.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not too hung up on any specific approach on the content-tag side; it just makes me nervous to be encoding assumptions here that could break at any time 😅

type: 'expression' | 'class-member';
tagName: 'template';
contents: string;
range: {
start: number;
end: number;
};
contentRange: {
start: number;
end: number;
};
startRange: {
end: number;
start: number;
};
endRange: {
start: number;
end: number;
};
}

export const preprocess: GlintExtensionPreprocess<PreprocessData> = (source, path) => {
let templates = parseTemplates(source, path, { templateTag: 'template' }).filter(
(match) => match.type === 'template-tag'
);
let templates = p.parse(source, path) as Parsed[];

let templateLocations: Array<TemplateLocation> = [];
let segments: Array<string> = [];
let sourceOffset = 0;
let delta = 0;

for (let template of templates) {
let { start, end } = template;
let startTagLength = template.start[0].length;
let endTagLength = template.end[0].length;
let startTagOffset = start.index ?? -1;
let endTagOffset = end.index ?? -1;
let startTagLength = template.startRange.end - template.startRange.start;
let endTagLength = template.endRange.end - template.endRange.start;
let startTagOffset = template.startRange.start;
let endTagOffset = template.endRange.start;

if (startTagOffset === -1 || endTagOffset === -1) continue;

Expand Down
7 changes: 4 additions & 3 deletions packages/environment-ember-template-imports/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,16 @@
"-private/**/*.{js,d.ts}",
"globals/index.d.ts"
],
"dependencies": {
"content-tag": "^1.1.0"
},
"peerDependencies": {
"@glint/environment-ember-loose": "^1.2.1",
"@glint/template": "^1.2.1",
"@types/ember__component": "^4.0.10",
"@types/ember__helper": "^4.0.1",
"@types/ember__modifier": "^4.0.3",
"@types/ember__routing": "^4.0.12",
"ember-template-imports": "^3.0.0"
"@types/ember__routing": "^4.0.12"
},
"peerDependenciesMeta": {
"@types/ember__component": {
Expand All @@ -55,7 +57,6 @@
"@types/ember__helper": "^4.0.1",
"@types/ember__modifier": "^4.0.3",
"@types/ember__routing": "^4.0.12",
"ember-template-imports": "^3.0.0",
"vitest": "^0.22.0"
},
"publishConfig": {
Expand Down
7 changes: 6 additions & 1 deletion yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -5151,6 +5151,11 @@ [email protected]:
dependencies:
safe-buffer "5.2.1"

content-tag@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/content-tag/-/content-tag-1.1.0.tgz#fcef4bdcf1850f9b67bf0b6f7aee217c6d7ea9fa"
integrity sha512-bktivDORs9M890KwVKrIONYvHhwshfgF4b1G/TFPrjH12Ag2GDiSdxVHqIzMxWZ297VgIRPSImURlpcOzJP/LQ==

content-type@~1.0.4:
version "1.0.4"
resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b"
Expand Down Expand Up @@ -6461,7 +6466,7 @@ ember-template-imports@^1.1.1:
ember-cli-version-checker "^5.1.2"
validate-peer-dependencies "^1.1.0"

ember-template-imports@^3.0.0, ember-template-imports@^3.1.1:
ember-template-imports@^3.1.1:
version "3.4.0"
resolved "https://registry.yarnpkg.com/ember-template-imports/-/ember-template-imports-3.4.0.tgz#c40757e2d41e289ce08c0fe80671000bf216e0ef"
integrity sha512-3Cwcj3NXA129g3ZhmrQ/nYOxksFonTmB/qxyaSNTHrLBSoc93UZys47hBz13DlcfoeSCCrNt2Qpq1j890I04PQ==
Expand Down