656a821a94
Migrate expressions, workflow-parser, and languageservice packages to use proper ESM imports with .js extensions that work with node16 moduleResolution. Changes: - Update tsconfig.build.json in each package to use module: node16 and moduleResolution: node16 - Add .js extensions to all relative import paths (Option B approach) - Fix yaml internal type imports in workflow-parser by defining local types - Add skipLibCheck to handle @types/node compatibility issues - Add TypeScript 5.8.3 override in root package.json - Add ESM migration plan documentation The languageserver package is deferred due to test hang issues that need further investigation. Related #154 - Upgrade moduleResolution from node to node16 or nodenext Related #110 - Published ESM code has imports without file extensions Related #64 - expressions: ERR_MODULE_NOT_FOUND attempting to run example Related #146 - Can not import @actions/workflow-parser Test results: - expressions: 1068 tests passed - workflow-parser: 292 tests passed - languageservice: 452 tests passed * docs: update ESM migration plan with findings - Update languageserver blocker: vscode-languageserver v8.0.2 lacks ESM exports (not a test hang issue) - Document that Option B (manual .js extensions) was chosen over Option A due to ts-jest compatibility issues - Add workaround for yaml package internal types (LinePos, NodeBase) - Update migration status table with accurate reason for deferral - Add skipLibCheck note for @types/node compatibility
202 lines
4.3 KiB
TypeScript
202 lines
4.3 KiB
TypeScript
import {Lexer, Token, TokenType} from "./lexer.js";
|
|
|
|
describe("lexer", () => {
|
|
const tests: {
|
|
input: string;
|
|
tokenTypes: TokenType[];
|
|
tokens?: Token[];
|
|
}[] = [
|
|
{input: "<", tokenTypes: [TokenType.LESS]},
|
|
{input: ">", tokenTypes: [TokenType.GREATER]},
|
|
|
|
{input: "!=", tokenTypes: [TokenType.BANG_EQUAL]},
|
|
{input: "==", tokenTypes: [TokenType.EQUAL_EQUAL]},
|
|
{input: "<=", tokenTypes: [TokenType.LESS_EQUAL]},
|
|
{input: ">=", tokenTypes: [TokenType.GREATER_EQUAL]},
|
|
|
|
{input: "&&", tokenTypes: [TokenType.AND]},
|
|
{input: "||", tokenTypes: [TokenType.OR]},
|
|
|
|
// Numbers
|
|
{input: "12", tokenTypes: [TokenType.NUMBER]},
|
|
{input: "12.0", tokenTypes: [TokenType.NUMBER]},
|
|
{input: "0", tokenTypes: [TokenType.NUMBER]},
|
|
{input: "-0", tokenTypes: [TokenType.NUMBER]},
|
|
{input: "-12.0", tokenTypes: [TokenType.NUMBER]},
|
|
|
|
// Strings
|
|
{input: "'It''s okay'", tokenTypes: [TokenType.STRING]},
|
|
{
|
|
input: "format('{0} == ''queued''', needs)",
|
|
tokenTypes: [
|
|
TokenType.IDENTIFIER,
|
|
TokenType.LEFT_PAREN,
|
|
TokenType.STRING,
|
|
TokenType.COMMA,
|
|
TokenType.IDENTIFIER,
|
|
TokenType.RIGHT_PAREN
|
|
]
|
|
},
|
|
|
|
// Arrays
|
|
{
|
|
input: "[1,2]",
|
|
tokenTypes: [TokenType.LEFT_BRACKET, TokenType.NUMBER, TokenType.COMMA, TokenType.NUMBER, TokenType.RIGHT_BRACKET]
|
|
},
|
|
|
|
// Simple expressions
|
|
{
|
|
input: "1 == 2",
|
|
tokenTypes: [TokenType.NUMBER, TokenType.EQUAL_EQUAL, TokenType.NUMBER]
|
|
},
|
|
{
|
|
input: "1== 1",
|
|
tokenTypes: [TokenType.NUMBER, TokenType.EQUAL_EQUAL, TokenType.NUMBER]
|
|
},
|
|
{
|
|
input: "1< 1",
|
|
tokenTypes: [TokenType.NUMBER, TokenType.LESS, TokenType.NUMBER]
|
|
},
|
|
|
|
// Identifiers
|
|
{
|
|
input: "github",
|
|
tokenTypes: [TokenType.IDENTIFIER],
|
|
tokens: [
|
|
{
|
|
type: TokenType.IDENTIFIER,
|
|
lexeme: "github",
|
|
range: {
|
|
start: {
|
|
line: 0,
|
|
column: 0
|
|
},
|
|
end: {
|
|
line: 0,
|
|
column: 6
|
|
}
|
|
}
|
|
}
|
|
]
|
|
},
|
|
|
|
// Keywords
|
|
{
|
|
input: "true",
|
|
tokenTypes: [TokenType.TRUE],
|
|
tokens: [
|
|
{
|
|
type: TokenType.TRUE,
|
|
lexeme: "true",
|
|
range: {
|
|
start: {
|
|
line: 0,
|
|
column: 0
|
|
},
|
|
end: {
|
|
line: 0,
|
|
column: 4
|
|
}
|
|
}
|
|
}
|
|
]
|
|
},
|
|
|
|
{
|
|
input: "false",
|
|
tokenTypes: [TokenType.FALSE],
|
|
tokens: [
|
|
{
|
|
type: TokenType.FALSE,
|
|
lexeme: "false",
|
|
range: {
|
|
start: {
|
|
line: 0,
|
|
column: 0
|
|
},
|
|
end: {
|
|
line: 0,
|
|
column: 5
|
|
}
|
|
}
|
|
}
|
|
]
|
|
},
|
|
|
|
{
|
|
input: "null",
|
|
tokenTypes: [TokenType.NULL],
|
|
tokens: [
|
|
{
|
|
type: TokenType.NULL,
|
|
lexeme: "null",
|
|
range: {
|
|
start: {
|
|
line: 0,
|
|
column: 0
|
|
},
|
|
end: {
|
|
line: 0,
|
|
column: 4
|
|
}
|
|
}
|
|
}
|
|
]
|
|
},
|
|
|
|
{
|
|
input: "github\n ==",
|
|
tokenTypes: [TokenType.IDENTIFIER, TokenType.EQUAL_EQUAL],
|
|
tokens: [
|
|
{
|
|
type: TokenType.IDENTIFIER,
|
|
lexeme: "github",
|
|
range: {
|
|
start: {
|
|
line: 0,
|
|
column: 0
|
|
},
|
|
end: {
|
|
line: 0,
|
|
column: 6
|
|
}
|
|
}
|
|
},
|
|
{
|
|
type: TokenType.EQUAL_EQUAL,
|
|
lexeme: "==",
|
|
range: {
|
|
start: {
|
|
line: 1,
|
|
column: 1
|
|
},
|
|
end: {
|
|
line: 1,
|
|
column: 3
|
|
}
|
|
}
|
|
}
|
|
]
|
|
}
|
|
];
|
|
|
|
test.each(tests)(
|
|
"$input",
|
|
({input, tokenTypes, tokens}: {input: string; tokenTypes: TokenType[]; tokens?: Token[]}) => {
|
|
const l = new Lexer(input);
|
|
|
|
const r = l.lex();
|
|
|
|
const got = r.tokens.map(t => t.type);
|
|
|
|
tokenTypes.push(TokenType.EOF);
|
|
expect(got).toEqual(tokenTypes);
|
|
|
|
if (tokens) {
|
|
// Ignore the last EOF token
|
|
expect(r.tokens.slice(0, r.tokens.length - 1)).toEqual(tokens);
|
|
}
|
|
}
|
|
);
|
|
});
|