Files
languageservices/expressions/src/lexer.test.ts
T
eric sciple 656a821a94 ESM migration: Add .js extensions for node16 moduleResolution (#257)
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
2025-12-18 13:35:48 -06:00

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);
}
}
);
});