Skip to content

Commit

Permalink
Introduce parser concepts for import.meta.resolve(…).
Browse files Browse the repository at this point in the history
  • Loading branch information
lgarron committed Sep 12, 2024
1 parent 29d92ed commit a36bf32
Show file tree
Hide file tree
Showing 10 changed files with 84 additions and 7 deletions.
1 change: 1 addition & 0 deletions .temp/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
*.js
6 changes: 6 additions & 0 deletions .temp/test/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
This is a temporary example test. Run using:

```shell
hereby local
node ./built/local/tsc.js --project ".temp/test/tsconfig.json"
```
3 changes: 3 additions & 0 deletions .temp/test/main.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
const entry = import.meta.resolve("./worker")
const worker = new Worker(entry, {type: "module"});
worker.addEventListener("message", console.log);
4 changes: 4 additions & 0 deletions .temp/test/plus.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export function plus(a: number, b: number): number {
console.log(import.meta)
return a + b;
}
8 changes: 8 additions & 0 deletions .temp/test/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"compilerOptions": {
"target": "es2022",
"module": "es2022",
"moduleResolution": "bundler"
},
"files": ["./main.ts", "./worker.ts"]
}
7 changes: 7 additions & 0 deletions .temp/test/worker.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
(async () => {
postMessage({
"hi": "world",
"import.meta.url": import.meta.url,
"sum": (await import("./plus")).plus(2, 3)
})
})()
38 changes: 34 additions & 4 deletions src/compiler/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5918,11 +5918,40 @@ namespace Parser {
expression = parseTokenNode<PrimaryExpression>();
}
else if (lookAhead(nextTokenIsDot)) {
// This is an 'import.*' metaproperty (i.e. 'import.meta')
// This is either specificallly an `import.meta.resolve(…)` call or more generally an 'import.*' metaproperty (i.e. 'import.meta')
nextToken(); // advance past the 'import'
nextToken(); // advance past the dot
expression = finishNode(factory.createMetaProperty(SyntaxKind.ImportKeyword, parseIdentifierName()), pos);
sourceFlags |= NodeFlags.PossiblyContainsImportMeta;
const identifierName = parseIdentifierName();

// TODO: use `parseRightSideOfDot`?
const foundImportMetaResolve = tryParse(() => {
// `meta` and `resolve` are not JavaScript keywords, so we attempt to look for the direct string identifiers to recognizer `import.meta.resolve(…)`.
if (identifierName.escapedText === "meta") {
if (token() === SyntaxKind.DotToken) {
nextToken(); // advance past the dot
const nextIdentifierName = lookAhead(parseIdentifierName);
if (nextIdentifierName.escapedText === "resolve") {
if (lookAhead(() => nextToken() === SyntaxKind.OpenParenToken)) {
return true;
}
}
}

}
return false;
});

if (foundImportMetaResolve) {
sourceFlags |= NodeFlags.PossiblyContainsDynamicImport;

expression = {} as any; // TODO: calculate the expression. Here are some failed attempts:
// expression = finishNode(factoryCreateCallExpression(factoryCreateExpressionStatement(), undefined, parseArgumentList()), pos) as any;
// expression = parseTokenNode<PrimaryExpression>();
// expression = finishNode(factoryCreateToken(SyntaxKind.ImportMetaResolveExpression), getNodePos()) as PrimaryExpression;
} else {
expression = finishNode(factory.createMetaProperty(SyntaxKind.ImportKeyword, identifierName), pos);
sourceFlags |= NodeFlags.PossiblyContainsImportMeta;
}
}
else {
expression = parseMemberExpressionOrHigher();
Expand All @@ -5935,7 +5964,8 @@ namespace Parser {
// Now, we *may* be complete. However, we might have consumed the start of a
// CallExpression or OptionalExpression. As such, we need to consume the rest
// of it here to be complete.
return parseCallExpressionRest(pos, expression);
const rest = parseCallExpressionRest(pos, expression);
return rest;
}

function parseMemberExpressionOrHigher(): MemberExpression {
Expand Down
8 changes: 5 additions & 3 deletions src/compiler/program.ts
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,7 @@ import {
isImportCall,
isImportDeclaration,
isImportEqualsDeclaration,
isImportMetaResolveCall,
isImportSpecifier,
isImportTypeNode,
isInJSFile,
Expand Down Expand Up @@ -3482,7 +3483,7 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg
}

if ((file.flags & NodeFlags.PossiblyContainsDynamicImport) || isJavaScriptFile) {
collectDynamicImportOrRequireOrJsDocImportCalls(file);
collectDynamicImportOrSimilarCalls(file);
}

file.imports = imports || emptyArray;
Expand Down Expand Up @@ -3546,7 +3547,7 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg
}
}

function collectDynamicImportOrRequireOrJsDocImportCalls(file: SourceFile) {
function collectDynamicImportOrSimilarCalls(file: SourceFile) {
const r = /import|require/g;
while (r.exec(file.text) !== null) { // eslint-disable-line no-restricted-syntax
const node = getNodeAtPosition(file, r.lastIndex);
Expand All @@ -3555,7 +3556,8 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg
imports = append(imports, node.arguments[0]);
}
// we have to check the argument list has length of at least 1. We will still have to process these even though we have parsing error.
else if (isImportCall(node) && node.arguments.length >= 1 && isStringLiteralLike(node.arguments[0])) {
else if ((isImportCall(node) || isImportMetaResolveCall(node)) && node.arguments.length >= 1 && isStringLiteralLike(node.arguments[0])) {
console.log("isImportMetaResolveCall mbyybyey", node)
setParentRecursive(node, /*incremental*/ false); // we need parent data on imports before the program is fully bound, so we ensure it's set here
imports = append(imports, node.arguments[0]);
}
Expand Down
9 changes: 9 additions & 0 deletions src/compiler/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,7 @@ export const enum SyntaxKind {
AsExpression,
NonNullExpression,
MetaProperty,
ImportMetaResolveExpression,
SyntheticExpression,
SatisfiesExpression,

Expand Down Expand Up @@ -2479,6 +2480,10 @@ export interface ImportExpression extends PrimaryExpression {
readonly kind: SyntaxKind.ImportKeyword;
}

export interface ImportMetaResolveExpression extends PrimaryExpression {
readonly kind: SyntaxKind.ImportMetaResolveExpression;
}

export interface DeleteExpression extends UnaryExpression {
readonly kind: SyntaxKind.DeleteExpression;
readonly expression: UnaryExpression;
Expand Down Expand Up @@ -3097,6 +3102,10 @@ export interface ImportCall extends CallExpression {
readonly expression: ImportExpression;
}

export interface ImportMetaResolveCall extends CallExpression {
readonly expression: ImportMetaResolveExpression;
}

export interface ExpressionWithTypeArguments extends MemberExpression, NodeWithTypeArguments {
readonly kind: SyntaxKind.ExpressionWithTypeArguments;
readonly expression: LeftHandSideExpression;
Expand Down
7 changes: 7 additions & 0 deletions src/compiler/utilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,7 @@ import {
ImportDeclaration,
ImportEqualsDeclaration,
ImportMetaProperty,
ImportMetaResolveCall,
ImportSpecifier,
ImportTypeNode,
IndexInfo,
Expand Down Expand Up @@ -2559,6 +2560,12 @@ export function isImportCall(n: Node): n is ImportCall {
return n.kind === SyntaxKind.CallExpression && (n as CallExpression).expression.kind === SyntaxKind.ImportKeyword;
}


/** @internal */
export function isImportMetaResolveCall(n: Node): n is ImportMetaResolveCall {
return n.kind === SyntaxKind.CallExpression && (n as CallExpression).expression.kind === SyntaxKind.ImportMetaResolveExpression;
}

/** @internal */
export function isImportMeta(n: Node): n is ImportMetaProperty {
return isMetaProperty(n)
Expand Down

0 comments on commit a36bf32

Please sign in to comment.