chore: use dprint to format js, json and markdown

This commit is contained in:
Boshen committed 2024-09-08 13:24:58 +08:00
1 parent be63a2cafc
commit d00a1f6511
49 files changed
+1489 -1526

No files matched your search

+12 -13
View File
@@ -17,23 +17,23 @@ diverse, inclusive, and healthy community.
Examples of behavior that contributes to a positive environment for our
community include:
* Demonstrating empathy and kindness toward other people
* Being respectful of differing opinions, viewpoints, and experiences
* Giving and gracefully accepting constructive feedback
* Accepting responsibility and apologizing to those affected by our mistakes,
- Demonstrating empathy and kindness toward other people
- Being respectful of differing opinions, viewpoints, and experiences
- Giving and gracefully accepting constructive feedback
- Accepting responsibility and apologizing to those affected by our mistakes,
and learning from the experience
* Focusing on what is best not just for us as individuals, but for the
- Focusing on what is best not just for us as individuals, but for the
overall community
Examples of unacceptable behavior include:
* The use of sexualized language or imagery, and sexual attention or
- The use of sexualized language or imagery, and sexual attention or
advances of any kind
* Trolling, insulting or derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or email
- Trolling, insulting or derogatory comments, and personal or political attacks
- Public or private harassment
- Publishing others' private information, such as a physical or email
address, without their explicit permission
* Other conduct which could reasonably be considered inappropriate in a
- Other conduct which could reasonably be considered inappropriate in a
professional setting
## Enforcement Responsibilities
@@ -105,7 +105,7 @@ Violating these terms may lead to a permanent ban.
### 4. Permanent Ban
**Community Impact**: Demonstrating a pattern of violation of community
standards, including sustained inappropriate behavior, harassment of an
standards, including sustained inappropriate behavior, harassment of an
individual, or aggression toward or disparagement of classes of individuals.
**Consequence**: A permanent ban from any sort of public interaction within
@@ -117,8 +117,7 @@ This Code of Conduct is adapted from the [Contributor Covenant][homepage],
version 2.0, available at
https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
Community Impact Guidelines were inspired by [Mozilla's code of conduct
enforcement ladder](https://github.com/mozilla/diversity).
Community Impact Guidelines were inspired by [Mozilla's code of conduct enforcement ladder](https://github.com/mozilla/diversity).
[homepage]: https://www.contributor-covenant.org
-3
View File
@@ -3,7 +3,4 @@ name: Bug report
about: Create a report to help us improve.
title: ''
labels: C-bug
---
@@ -3,7 +3,4 @@ name: Feature request
about: Suggest an idea for this project.
title: ''
labels: C-enhancement
---
+1 -1
View File
@@ -4,4 +4,4 @@
## Reporting a Vulnerability
If you find any potential vulnerability, join our [discord channel](https://discord.gg/9uXCAwqQZW) and contact Boshen.
If you find any potential vulnerability, join our [discord channel](https://discord.gg/9uXCAwqQZW) and contact Boshen.
+13 -13
View File
@@ -1,19 +1,19 @@
// A launch configuration that compiles the extension and then opens it inside a new window
{
"version": "0.2.0",
"configurations": [
{
"type": "extensionHost",
"request": "launch",
"name": "Launch Client",
"runtimeExecutable": "${execPath}",
"args": ["--extensionDevelopmentPath=${workspaceFolder}/editors/vscode" ],
"sourceMaps": true,
"outFiles": ["${workspaceFolder}/editors/vscode/dist/*.js"],
"env": {
"version": "0.2.0",
"configurations": [
{
"type": "extensionHost",
"request": "launch",
"name": "Launch Client",
"runtimeExecutable": "${execPath}",
"args": ["--extensionDevelopmentPath=${workspaceFolder}/editors/vscode"],
"sourceMaps": true,
"outFiles": ["${workspaceFolder}/editors/vscode/dist/*.js"],
"env": {
"SERVER_PATH_DEV": "${workspaceRoot}/target/debug/oxc_language_server",
"RUST_LOG": "debug"
}
}
]
}
]
}
+72 -72
View File
@@ -1,76 +1,76 @@
// See https://go.microsoft.com/fwlink/?LinkId=733558
// for the documentation about the tasks.json format
{
"version": "2.0.0",
"tasks": [
// format code, then build tests (but do not run them).
{
"label": "rust: cargo test --no-run",
"type": "cargo",
"command": "test",
"args": [
"--no-run"
],
"problemMatcher": [
"$rustc"
],
"group": {
"kind": "build",
"isDefault": true
},
"presentation": {
"echo": true,
"reveal": "silent",
"focus": false,
"panel": "shared",
"showReuseMessage": false,
"clear": true
},
"dependsOn": [
"rust: cargo fmt"
]
},
{
"type": "cargo",
"command": "fmt",
"problemMatcher": [
"$rustc"
],
"presentation": {
"echo": true,
"reveal": "silent",
"focus": false,
"panel": "shared",
"showReuseMessage": false,
"clear": false
},
"label": "rust: cargo fmt"
},
{
"type": "npm",
"script": "compile",
"group": "build",
"presentation": {
"panel": "dedicated",
"reveal": "never"
},
"problemMatcher": [
"$tsc"
]
},
{
"type": "shell",
"command": "cd ./editors/vscode && npm run watch",
"windows": {
"command": "cd ./editors/vscode; npm run watch"
},
"label": "watch",
"isBackground": true,
"group": "build",
"presentation": {
"panel": "dedicated",
"reveal": "never"
}
},
],
"version": "2.0.0",
"tasks": [
// format code, then build tests (but do not run them).
{
"label": "rust: cargo test --no-run",
"type": "cargo",
"command": "test",
"args": [
"--no-run"
],
"problemMatcher": [
"$rustc"
],
"group": {
"kind": "build",
"isDefault": true
},
"presentation": {
"echo": true,
"reveal": "silent",
"focus": false,
"panel": "shared",
"showReuseMessage": false,
"clear": true
},
"dependsOn": [
"rust: cargo fmt"
]
},
{
"type": "cargo",
"command": "fmt",
"problemMatcher": [
"$rustc"
],
"presentation": {
"echo": true,
"reveal": "silent",
"focus": false,
"panel": "shared",
"showReuseMessage": false,
"clear": false
},
"label": "rust: cargo fmt"
},
{
"type": "npm",
"script": "compile",
"group": "build",
"presentation": {
"panel": "dedicated",
"reveal": "never"
},
"problemMatcher": [
"$tsc"
]
},
{
"type": "shell",
"command": "cd ./editors/vscode && npm run watch",
"windows": {
"command": "cd ./editors/vscode; npm run watch"
},
"label": "watch",
"isBackground": true,
"group": "build",
"presentation": {
"panel": "dedicated",
"reveal": "never"
}
}
]
}
+3 -3
View File
@@ -2,12 +2,12 @@
### Release Oxlint
* Run [Prepare Release Oxlint] https://github.com/oxc-project/oxc/actions/workflows/prepare_release_oxlint.yml
- Run [Prepare Release Oxlint] https://github.com/oxc-project/oxc/actions/workflows/prepare_release_oxlint.yml
### E2E Testing
* Run [Oxlint Ecosystem CI](https://github.com/oxc-project/oxlint-ecosystem-ci/actions/workflows/ecosystem-ci.yml)
- Run [Oxlint Ecosystem CI](https://github.com/oxc-project/oxlint-ecosystem-ci/actions/workflows/ecosystem-ci.yml)
## Release Crates
* Run [Prepare Release Crates] https://github.com/oxc-project/oxc/actions/workflows/prepare_release_crates.yml
- Run [Prepare Release Crates] https://github.com/oxc-project/oxc/actions/workflows/prepare_release_crates.yml
+9 -9
View File
@@ -213,13 +213,13 @@ We spend half of our time on strengthening the test infrastructure to prevent pr
[Test Infrastructure](https://oxc.rs/docs/learn/architecture/test.html) documents our test procedures:
* Conformance suite on Test262, Babel, TypeScript
* Lots of fuzzing
* Linter snapshot diagnostics
* oxlint ecosystem ci
* Idempotency testing
* Code coverage
* End to end 3000 top npm packages
- Conformance suite on Test262, Babel, TypeScript
- Lots of fuzzing
- Linter snapshot diagnostics
- oxlint ecosystem ci
- Idempotency testing
- Code coverage
- End to end 3000 top npm packages
---
@@ -252,8 +252,8 @@ This project was incubated with the assistance of these exceptional mentors and
Special thanks go to
* [@domonji](https://github.com/domonji) for bootstrapping this project together, and also completing the TypeScript parser.
* [@tongtong-lu](https://github.com/tongtong-lu) and [@guan-wy](https://github.com/guan-wy) for designing the [project logo](https://github.com/oxc-project/oxc-assets).
- [@domonji](https://github.com/domonji) for bootstrapping this project together, and also completing the TypeScript parser.
- [@tongtong-lu](https://github.com/tongtong-lu) and [@guan-wy](https://github.com/guan-wy) for designing the [project logo](https://github.com/oxc-project/oxc-assets).
## ❤ Who's [Sponsoring Oxc](https://github.com/sponsors/Boshen)?
-1
View File
@@ -5,4 +5,3 @@ Implements ECMAScript® 2024 Language Specification
- https://tc39.es/ecma262/2024/multipage/ecmascript-language-lexical-grammar.html#sec-literals-regular-expression-literals
- https://tc39.es/ecma262/2024/multipage/text-processing.html#sec-regexp-regular-expression-objects
- https://tc39.es/ecma262/2024/multipage/additional-ecmascript-features-for-web-browsers.html#sec-regular-expressions-patterns
+12 -12
View File
@@ -41,10 +41,10 @@ For a maintainable and understandable codebase, please go big on code comments.
Each transform should include a comment at top of file including:
* High level explanation of what transform does.
* One "before / after" example.
* Link to Babel plugin.
* Note of any ways in which our implementation diverges from Babel's, and why.
- High level explanation of what transform does.
- One "before / after" example.
- Link to Babel plugin.
- Note of any ways in which our implementation diverges from Babel's, and why.
#### Methods
@@ -88,17 +88,17 @@ So where we feel Babel's implementation is inefficient, but we have to follow it
tests, make a `// TODO(improve-on-babel): Babel's impl is inefficient because X, we could do better by Y`
comment, so we can return to it later.
### Clear "entry points"
### Clear "entry points"
"Entry points" are where the visitor calls into the transform.
* Entry points of transform should be implemented as `impl Traverse for MyTransform`.
* Those methods have to be called `enter_*` and `exit_*`.
* Parent transform will only interface with child transform via these entry points.
* Only other method exposed externally should be `new`. That should be at top of the file.
* Entry points go directly below `new` method definition.
* Internal methods implemented lower down in an `impl MyTransform` block.
* Internal methods named descriptively - `add_id_to_function` not `transform_function`.
- Entry points of transform should be implemented as `impl Traverse for MyTransform`.
- Those methods have to be called `enter_*` and `exit_*`.
- Parent transform will only interface with child transform via these entry points.
- Only other method exposed externally should be `new`. That should be at top of the file.
- Entry points go directly below `new` method definition.
- Internal methods implemented lower down in an `impl MyTransform` block.
- Internal methods named descriptively - `add_id_to_function` not `transform_function`.
i.e. File is laid out so logic flows from top of file to bottom.
+16 -16
View File
@@ -10,22 +10,22 @@
* We should do this properly with a Rust build script using `syn` etc.
*/
import {writeFile} from 'fs/promises';
import {exec} from 'child_process';
import {join as pathJoin} from 'path';
import {fileURLToPath} from 'url';
import {promisify} from 'util';
import getTypesFromCode from './lib/parse.mjs';
import generateTraverseTraitCode from './lib/traverse.mjs';
import { exec } from 'child_process';
import { writeFile } from 'fs/promises';
import { join as pathJoin } from 'path';
import { fileURLToPath } from 'url';
import { promisify } from 'util';
import generateAncestorsCode from './lib/ancestor.mjs';
import generateWalkFunctionsCode from './lib/walk.mjs';
import getTypesFromCode from './lib/parse.mjs';
import generateScopesCollectorCode from './lib/scopes_collector.mjs';
import generateTraverseTraitCode from './lib/traverse.mjs';
import generateWalkFunctionsCode from './lib/walk.mjs';
const execAsync = promisify(exec);
const PREAMBLE = '// Auto-generated code, DO NOT EDIT DIRECTLY!\n'
+ '// Generated by `oxc_traverse/scripts/build.mjs`.\n'
+ '// To alter this generated file you have to edit the codegen.\n\n';
const PREAMBLE = '// Auto-generated code, DO NOT EDIT DIRECTLY!\n' +
'// Generated by `oxc_traverse/scripts/build.mjs`.\n' +
'// To alter this generated file you have to edit the codegen.\n\n';
const types = await getTypesFromCode();
@@ -36,9 +36,9 @@ await writeToFile('walk.rs', generateWalkFunctionsCode(types));
await writeToFile('scopes_collector.rs', generateScopesCollectorCode(types));
async function writeToFile(filename, code) {
code = `${PREAMBLE}${code}`;
const path = pathJoin(outputDirPath, filename);
console.log('Writing:', path);
await writeFile(path, code);
await execAsync(`rustfmt ${JSON.stringify(path)}`);
code = `${PREAMBLE}${code}`;
const path = pathJoin(outputDirPath, filename);
console.log('Writing:', path);
await writeFile(path, code);
await execAsync(`rustfmt ${JSON.stringify(path)}`);
}
+51 -51
View File
@@ -1,37 +1,37 @@
import {camelToSnake, snakeToCamel} from './utils.mjs';
import { camelToSnake, snakeToCamel } from './utils.mjs';
export default function generateAncestorsCode(types) {
const variantNamesForEnums = Object.create(null);
let ancestorTypeEnumVariants = '',
ancestorEnumVariants = '',
isFunctions = '',
ancestorTypes = '',
discriminant = 1;
for (const type of Object.values(types)) {
if (type.kind === 'enum') continue;
const variantNamesForEnums = Object.create(null);
let ancestorTypeEnumVariants = '',
ancestorEnumVariants = '',
isFunctions = '',
ancestorTypes = '',
discriminant = 1;
for (const type of Object.values(types)) {
if (type.kind === 'enum') continue;
const typeSnakeName = camelToSnake(type.name),
typeScreamingName = typeSnakeName.toUpperCase();
let offsetCode = '';
for (const field of type.fields) {
const offsetVarName = `OFFSET_${typeScreamingName}_${field.name.toUpperCase()}`;
field.offsetVarName = offsetVarName;
offsetCode += `pub(crate) const ${offsetVarName}: usize = `
+ `offset_of!(${type.name}, ${field.rawName});\n`;
}
const typeSnakeName = camelToSnake(type.name),
typeScreamingName = typeSnakeName.toUpperCase();
let offsetCode = '';
for (const field of type.fields) {
const offsetVarName = `OFFSET_${typeScreamingName}_${field.name.toUpperCase()}`;
field.offsetVarName = offsetVarName;
offsetCode += `pub(crate) const ${offsetVarName}: usize = ` +
`offset_of!(${type.name}, ${field.rawName});\n`;
}
const variantNames = [];
let thisAncestorTypes = '';
for (const field of type.fields) {
const fieldTypeName = field.innerTypeName,
fieldType = types[fieldTypeName];
if (!fieldType) continue;
const variantNames = [];
let thisAncestorTypes = '';
for (const field of type.fields) {
const fieldTypeName = field.innerTypeName,
fieldType = types[fieldTypeName];
if (!fieldType) continue;
let methodsCode = '';
for (const otherField of type.fields) {
if (otherField === field) continue;
let methodsCode = '';
for (const otherField of type.fields) {
if (otherField === field) continue;
methodsCode += `
methodsCode += `
#[inline]
pub fn ${otherField.rawName}(self) -> &'t ${otherField.rawTypeName} {
unsafe {
@@ -42,13 +42,13 @@ export default function generateAncestorsCode(types) {
}
}
`;
}
}
const fieldNameCamel = snakeToCamel(field.name),
lifetimes = type.rawName.length > type.name.length ? `<'a, 't>` : "<'t>",
structName = `${type.name}Without${fieldNameCamel}${lifetimes}`;
const fieldNameCamel = snakeToCamel(field.name),
lifetimes = type.rawName.length > type.name.length ? `<'a, 't>` : "<'t>",
structName = `${type.name}Without${fieldNameCamel}${lifetimes}`;
thisAncestorTypes += `
thisAncestorTypes += `
#[repr(transparent)]
#[derive(Clone, Copy, Debug)]
pub struct ${structName}(
@@ -61,44 +61,44 @@ export default function generateAncestorsCode(types) {
}
`;
const variantName = `${type.name}${fieldNameCamel}`;
variantNames.push(variantName);
const variantName = `${type.name}${fieldNameCamel}`;
variantNames.push(variantName);
ancestorTypeEnumVariants += `${variantName} = ${discriminant},\n`;
ancestorEnumVariants += `${variantName}(${structName}) = AncestorType::${variantName} as u16,\n`;
discriminant++;
ancestorTypeEnumVariants += `${variantName} = ${discriminant},\n`;
ancestorEnumVariants += `${variantName}(${structName}) = AncestorType::${variantName} as u16,\n`;
discriminant++;
if (fieldType.kind === 'enum') {
(variantNamesForEnums[fieldTypeName] || (variantNamesForEnums[fieldTypeName] = []))
.push(variantName);
}
}
if (fieldType.kind === 'enum') {
(variantNamesForEnums[fieldTypeName] || (variantNamesForEnums[fieldTypeName] = []))
.push(variantName);
}
}
if (variantNames.length > 0) {
ancestorTypes += `
if (variantNames.length > 0) {
ancestorTypes += `
${offsetCode}
${thisAncestorTypes}
`;
isFunctions += `
isFunctions += `
#[inline]
pub fn is_${typeSnakeName}(self) -> bool {
matches!(self, ${variantNames.map(name => `Self::${name}(_)`).join(' | ')})
}
`;
}
}
}
for (const [typeName, variantNames] of Object.entries(variantNamesForEnums)) {
isFunctions += `
for (const [typeName, variantNames] of Object.entries(variantNamesForEnums)) {
isFunctions += `
#[inline]
pub fn is_via_${camelToSnake(typeName)}(self) -> bool {
matches!(self, ${variantNames.map(name => `Self::${name}(_)`).join(' | ')})
}
`;
}
}
return `
return `
#![allow(
unsafe_code,
clippy::missing_safety_doc,
+184 -184
View File
@@ -1,7 +1,7 @@
import {readFile} from 'fs/promises';
import {join as pathJoin} from 'path';
import {fileURLToPath} from 'url';
import {typeAndWrappers} from './utils.mjs';
import { readFile } from 'fs/promises';
import { join as pathJoin } from 'path';
import { fileURLToPath } from 'url';
import { typeAndWrappers } from './utils.mjs';
const FILENAMES = ['js.rs', 'jsx.rs', 'literal.rs', 'ts.rs'];
@@ -9,226 +9,226 @@ const FILENAMES = ['js.rs', 'jsx.rs', 'literal.rs', 'ts.rs'];
* Parse type defs from Rust files.
*/
export default async function getTypesFromCode() {
const codeDirPath = pathJoin(fileURLToPath(import.meta.url), '../../../../oxc_ast/src/ast/');
const codeDirPath = pathJoin(fileURLToPath(import.meta.url), '../../../../oxc_ast/src/ast/');
const types = Object.create(null);
for (const filename of FILENAMES) {
const code = await readFile(`${codeDirPath}${filename}`, 'utf8');
parseFile(code, filename, types);
}
return types;
const types = Object.create(null);
for (const filename of FILENAMES) {
const code = await readFile(`${codeDirPath}${filename}`, 'utf8');
parseFile(code, filename, types);
}
return types;
}
class Position {
constructor(filename, index) {
this.filename = filename;
this.index = index;
}
constructor(filename, index) {
this.filename = filename;
this.index = index;
}
assert(condition, message) {
if (!condition) this.throw(message);
}
throw(message) {
throw new Error(`${message || 'Unknown error'} (at ${this.filename}:${this.index + 1})`);
}
assert(condition, message) {
if (!condition) this.throw(message);
}
throw(message) {
throw new Error(`${message || 'Unknown error'} (at ${this.filename}:${this.index + 1})`);
}
}
class Lines {
constructor(lines, filename, offset = 0) {
this.lines = lines;
this.filename = filename;
this.offset = offset;
this.index = 0;
}
constructor(lines, filename, offset = 0) {
this.lines = lines;
this.filename = filename;
this.offset = offset;
this.index = 0;
}
static fromCode(code, filename) {
const lines = code.split(/\r?\n/)
.map(line => line.replace(/\s+/g, ' ').replace(/ ?\/\/.*$/, '').replace(/ $/, ''));
return new Lines(lines, filename, 0);
}
static fromCode(code, filename) {
const lines = code.split(/\r?\n/)
.map(line => line.replace(/\s+/g, ' ').replace(/ ?\/\/.*$/, '').replace(/ $/, ''));
return new Lines(lines, filename, 0);
}
child() {
return new Lines([], this.filename, this.index);
}
child() {
return new Lines([], this.filename, this.index);
}
current() {
return this.lines[this.index];
}
next() {
return this.lines[this.index++];
}
isEnd() {
return this.index === this.lines.length;
}
current() {
return this.lines[this.index];
}
next() {
return this.lines[this.index++];
}
isEnd() {
return this.index === this.lines.length;
}
position() {
return new Position(this.filename, this.index + this.offset);
}
positionPrevious() {
return new Position(this.filename, this.index + this.offset - 1);
}
position() {
return new Position(this.filename, this.index + this.offset);
}
positionPrevious() {
return new Position(this.filename, this.index + this.offset - 1);
}
}
function parseFile(code, filename, types) {
const lines = Lines.fromCode(code, filename);
while (!lines.isEnd()) {
// if not #[ast(visit, ..)]
if (!/^#\[ast\(.*visit.*\)\]/.test(lines.current())) {
lines.next();
continue;
}
// Consume attrs and comments, parse `#[scope]` attr
let match, scopeArgs = null;
while (!lines.isEnd()) {
if (/^#\[scope[(\]]/.test(lines.current())) {
scopeArgs = parseScopeArgs(lines, scopeArgs);
continue;
}
match = lines.next().match(/^pub (enum|struct) ((.+?)(?:<'a>)?) \{/);
if (match) break;
}
lines.position().assert(match, `Could not find enum or struct after #[ast]`);
const [, kind, rawName, name] = match;
// Find end of struct / enum
const itemLines = lines.child();
while (!lines.isEnd()) {
const line = lines.next();
if (line === '}') break;
itemLines.lines.push(line.trim());
}
if (kind === 'struct') {
types[name] = parseStruct(name, rawName, itemLines, scopeArgs);
} else {
types[name] = parseEnum(name, rawName, itemLines);
}
const lines = Lines.fromCode(code, filename);
while (!lines.isEnd()) {
// if not #[ast(visit, ..)]
if (!/^#\[ast\(.*visit.*\)\]/.test(lines.current())) {
lines.next();
continue;
}
// Consume attrs and comments, parse `#[scope]` attr
let match, scopeArgs = null;
while (!lines.isEnd()) {
if (/^#\[scope[(\]]/.test(lines.current())) {
scopeArgs = parseScopeArgs(lines, scopeArgs);
continue;
}
match = lines.next().match(/^pub (enum|struct) ((.+?)(?:<'a>)?) \{/);
if (match) break;
}
lines.position().assert(match, `Could not find enum or struct after #[ast]`);
const [, kind, rawName, name] = match;
// Find end of struct / enum
const itemLines = lines.child();
while (!lines.isEnd()) {
const line = lines.next();
if (line === '}') break;
itemLines.lines.push(line.trim());
}
if (kind === 'struct') {
types[name] = parseStruct(name, rawName, itemLines, scopeArgs);
} else {
types[name] = parseEnum(name, rawName, itemLines);
}
}
}
function parseStruct(name, rawName, lines, scopeArgs) {
const fields = [];
const fields = [];
while (!lines.isEnd()) {
let isScopeEntry = false, line;
while (!lines.isEnd()) {
let isScopeEntry = false, line;
while (!lines.isEnd()) {
line = lines.next();
if (line === '') continue;
if (line === '#[scope(enter_before)]') {
isScopeEntry = true;
} else if (line.startsWith('#[')) {
while (!line.endsWith(']')) {
line = lines.next();
}
} else {
break;
}
line = lines.next();
if (line === '') continue;
if (line === '#[scope(enter_before)]') {
isScopeEntry = true;
} else if (line.startsWith('#[')) {
while (!line.endsWith(']')) {
line = lines.next();
}
const match = line.match(/^pub ((?:r#)?([a-z_]+)): (.+),$/);
lines.positionPrevious().assert(match, `Cannot parse line as struct field: '${line}'`);
const [, rawName, name, rawTypeName] = match,
typeName = rawTypeName.replace(/<'a>/g, '').replace(/<'a, ?/g, '<'),
{name: innerTypeName, wrappers} = typeAndWrappers(typeName);
fields.push({name, typeName, rawName, rawTypeName, innerTypeName, wrappers});
if (isScopeEntry) scopeArgs.enterScopeBefore = name;
} else {
break;
}
}
return {kind: 'struct', name, rawName, fields, scopeArgs};
const match = line.match(/^pub ((?:r#)?([a-z_]+)): (.+),$/);
lines.positionPrevious().assert(match, `Cannot parse line as struct field: '${line}'`);
const [, rawName, name, rawTypeName] = match,
typeName = rawTypeName.replace(/<'a>/g, '').replace(/<'a, ?/g, '<'),
{ name: innerTypeName, wrappers } = typeAndWrappers(typeName);
fields.push({ name, typeName, rawName, rawTypeName, innerTypeName, wrappers });
if (isScopeEntry) scopeArgs.enterScopeBefore = name;
}
return { kind: 'struct', name, rawName, fields, scopeArgs };
}
function parseEnum(name, rawName, lines) {
const variants = [],
inherits = [];
while (!lines.isEnd()) {
let line = lines.next();
if (line === '') continue;
if (line.startsWith('#[')) {
while (!line.endsWith(']')) {
line = lines.next();
}
continue;
}
const match = line.match(/^(.+?)\((.+?)\)(?: ?= ?(\d+))?,$/);
if (match) {
const [, name, rawTypeName, discriminantStr] = match,
typeName = rawTypeName.replace(/<'a>/g, '').replace(/<'a, ?/g, '<'),
{name: innerTypeName, wrappers} = typeAndWrappers(typeName),
discriminant = discriminantStr ? +discriminantStr : null;
variants.push({name, typeName, rawTypeName, innerTypeName, wrappers, discriminant});
} else {
const match2 = line.match(/^@inherit ([A-Za-z]+)$/);
lines.positionPrevious().assert(match2, `Cannot parse line as enum variant: '${line}'`);
inherits.push(match2[1]);
}
const variants = [],
inherits = [];
while (!lines.isEnd()) {
let line = lines.next();
if (line === '') continue;
if (line.startsWith('#[')) {
while (!line.endsWith(']')) {
line = lines.next();
}
continue;
}
return {kind: 'enum', name, rawName, variants, inherits};
const match = line.match(/^(.+?)\((.+?)\)(?: ?= ?(\d+))?,$/);
if (match) {
const [, name, rawTypeName, discriminantStr] = match,
typeName = rawTypeName.replace(/<'a>/g, '').replace(/<'a, ?/g, '<'),
{ name: innerTypeName, wrappers } = typeAndWrappers(typeName),
discriminant = discriminantStr ? +discriminantStr : null;
variants.push({ name, typeName, rawTypeName, innerTypeName, wrappers, discriminant });
} else {
const match2 = line.match(/^@inherit ([A-Za-z]+)$/);
lines.positionPrevious().assert(match2, `Cannot parse line as enum variant: '${line}'`);
inherits.push(match2[1]);
}
}
return { kind: 'enum', name, rawName, variants, inherits };
}
function parseScopeArgs(lines, scopeArgs) {
const position = lines.position();
const position = lines.position();
// Get whole of `#[scope]` attr text as a single line string
let scopeArgsStr = '';
let line = lines.next();
if (line !== '#[scope]') {
line = line.slice('#[scope('.length);
while (!line.endsWith(')]')) {
scopeArgsStr += ` ${line}`;
line = lines.next();
}
scopeArgsStr += ` ${line.slice(0, -2)}`;
scopeArgsStr = scopeArgsStr.trim().replace(/ +/g, ' ').replace(/,$/, '');
// Get whole of `#[scope]` attr text as a single line string
let scopeArgsStr = '';
let line = lines.next();
if (line !== '#[scope]') {
line = line.slice('#[scope('.length);
while (!line.endsWith(')]')) {
scopeArgsStr += ` ${line}`;
line = lines.next();
}
scopeArgsStr += ` ${line.slice(0, -2)}`;
scopeArgsStr = scopeArgsStr.trim().replace(/ +/g, ' ').replace(/,$/, '');
}
// Parse attr
return parseScopeArgsStr(scopeArgsStr, scopeArgs, position);
// Parse attr
return parseScopeArgsStr(scopeArgsStr, scopeArgs, position);
}
const SCOPE_ARGS_KEYS = {flags: 'flags', strict_if: 'strictIf'};
const SCOPE_ARGS_KEYS = { flags: 'flags', strict_if: 'strictIf' };
function parseScopeArgsStr(argsStr, args, position) {
if (!args) args = {flags: 'ScopeFlags::empty()', strictIf: null, enterScopeBefore: null};
if (!args) args = { flags: 'ScopeFlags::empty()', strictIf: null, enterScopeBefore: null };
if (!argsStr) return args;
if (!argsStr) return args;
const matchAndConsume = (regex) => {
const match = argsStr.match(regex);
position.assert(match);
argsStr = argsStr.slice(match[0].length);
return match.slice(1);
};
const matchAndConsume = (regex) => {
const match = argsStr.match(regex);
position.assert(match);
argsStr = argsStr.slice(match[0].length);
return match.slice(1);
};
try {
while (true) {
const [keyRaw] = matchAndConsume(/^([a-z_]+)\(/);
const key = SCOPE_ARGS_KEYS[keyRaw];
position.assert(key, `Unexpected scope macro arg: ${key}`);
try {
while (true) {
const [keyRaw] = matchAndConsume(/^([a-z_]+)\(/);
const key = SCOPE_ARGS_KEYS[keyRaw];
position.assert(key, `Unexpected scope macro arg: ${key}`);
let bracketCount = 1,
index = 0;
for (; index < argsStr.length; index++) {
const char = argsStr[index];
if (char === '(') {
bracketCount++;
} else if (char === ')') {
bracketCount--;
if (bracketCount === 0) break;
}
}
position.assert(bracketCount === 0);
args[key] = argsStr.slice(0, index).trim();
argsStr = argsStr.slice(index + 1);
if (argsStr === '') break;
matchAndConsume(/^ ?, ?/);
let bracketCount = 1,
index = 0;
for (; index < argsStr.length; index++) {
const char = argsStr[index];
if (char === '(') {
bracketCount++;
} else if (char === ')') {
bracketCount--;
if (bracketCount === 0) break;
}
} catch (err) {
position.throw(`Cannot parse scope args: '${argsStr}': ${err?.message || 'Unknown error'}`);
}
}
position.assert(bracketCount === 0);
return args;
args[key] = argsStr.slice(0, index).trim();
argsStr = argsStr.slice(index + 1);
if (argsStr === '') break;
matchAndConsume(/^ ?, ?/);
}
} catch (err) {
position.throw(`Cannot parse scope args: '${argsStr}': ${err?.message || 'Unknown error'}`);
}
return args;
}
@@ -1,20 +1,20 @@
import {camelToSnake} from './utils.mjs';
import { camelToSnake } from './utils.mjs';
export default function generateScopesCollectorCode(types) {
let methods = '';
for (const type of Object.values(types)) {
if (type.kind === 'enum' || !type.scopeArgs) continue;
let methods = '';
for (const type of Object.values(types)) {
if (type.kind === 'enum' || !type.scopeArgs) continue;
const extraParams = type.scopeArgs.flags === 'flags' ? ', _flags: ScopeFlags' : '';
methods += `
const extraParams = type.scopeArgs.flags === 'flags' ? ', _flags: ScopeFlags' : '';
methods += `
#[inline]
fn visit_${camelToSnake(type.name)}(&mut self, it: &${type.rawName}${extraParams}) {
self.add_scope(&it.scope_id);
}
`;
}
}
return `
return `
use std::cell::Cell;
#[allow(clippy::wildcard_imports)]
+9 -9
View File
@@ -1,21 +1,21 @@
import {camelToSnake} from './utils.mjs';
import { camelToSnake } from './utils.mjs';
export default function generateTraverseTraitCode(types) {
const typesArr = Object.values(types);
typesArr.push({name: 'Statements', rawName: "Vec<'a, Statement<'a>>"});
const typesArr = Object.values(types);
typesArr.push({ name: 'Statements', rawName: "Vec<'a, Statement<'a>>" });
let traverseMethods = '';
for (const type of typesArr) {
const snakeName = camelToSnake(type.name);
traverseMethods += `
let traverseMethods = '';
for (const type of typesArr) {
const snakeName = camelToSnake(type.name);
traverseMethods += `
#[inline]
fn enter_${snakeName}(&mut self, node: &mut ${type.rawName}, ctx: &mut TraverseCtx<'a>) {}
#[inline]
fn exit_${snakeName}(&mut self, node: &mut ${type.rawName}, ctx: &mut TraverseCtx<'a>) {}
`;
}
}
return `
return `
use oxc_allocator::Vec;
#[allow(clippy::wildcard_imports)]
use oxc_ast::ast::*;
+24 -24
View File
@@ -1,34 +1,34 @@
export function typeAndWrappers(name) {
const wrappers = [];
while (true) {
const match = name.match(/^(.+?)<(.+)>$/);
if (!match) break;
wrappers.push(match[1]);
name = match[2];
}
return {name, wrappers};
const wrappers = [];
while (true) {
const match = name.match(/^(.+?)<(.+)>$/);
if (!match) break;
wrappers.push(match[1]);
name = match[2];
}
return { name, wrappers };
}
export function camelToSnake(name) {
let prefixLen = 1;
for (const prefix of ['TS', 'JSX', 'JS']) {
if (name.startsWith(prefix)) {
prefixLen = prefix.length;
break;
}
let prefixLen = 1;
for (const prefix of ['TS', 'JSX', 'JS']) {
if (name.startsWith(prefix)) {
prefixLen = prefix.length;
break;
}
return name.slice(0, prefixLen).toLowerCase()
+ name.slice(prefixLen).replace(/[A-Z]/g, c => `_${c.toLowerCase()}`);
}
return name.slice(0, prefixLen).toLowerCase() +
name.slice(prefixLen).replace(/[A-Z]/g, c => `_${c.toLowerCase()}`);
}
export function snakeToCamel(name) {
let prefixLen = 0;
for (const prefix of ['TS', 'JSX', 'JS']) {
if (name.startsWith(`${prefix.toLowerCase()}_`)) {
prefixLen = prefix.length + 1;
break;
}
let prefixLen = 0;
for (const prefix of ['TS', 'JSX', 'JS']) {
if (name.startsWith(`${prefix.toLowerCase()}_`)) {
prefixLen = prefix.length + 1;
break;
}
return name.slice(0, prefixLen + 1).toUpperCase()
+ name.slice(prefixLen + 1).replace(/_([a-z])/g, (_, c) => c.toUpperCase());
}
return name.slice(0, prefixLen + 1).toUpperCase() +
name.slice(prefixLen + 1).replace(/_([a-z])/g, (_, c) => c.toUpperCase());
}
+137 -137
View File
@@ -1,17 +1,17 @@
import assert from 'assert';
import {camelToSnake, snakeToCamel} from './utils.mjs';
import { camelToSnake, snakeToCamel } from './utils.mjs';
export default function generateWalkFunctionsCode(types) {
let walkMethods = '';
for (const type of Object.values(types)) {
if (type.kind === 'struct') {
walkMethods += generateWalkForStruct(type, types);
} else {
walkMethods += generateWalkForEnum(type, types);
}
let walkMethods = '';
for (const type of Object.values(types)) {
if (type.kind === 'struct') {
walkMethods += generateWalkForStruct(type, types);
} else {
walkMethods += generateWalkForEnum(type, types);
}
}
return `
return `
#![allow(
unsafe_code,
clippy::missing_safety_doc,
@@ -47,83 +47,83 @@ export default function generateWalkFunctionsCode(types) {
}
function generateWalkForStruct(type, types) {
let scopeIdField;
const visitedFields = type.fields.filter(field => {
if (field.name === 'scope_id' && field.typeName === `Cell<Option<ScopeId>>`) {
scopeIdField = field;
}
return field.innerTypeName in types;
});
let scopeIdField;
const visitedFields = type.fields.filter(field => {
if (field.name === 'scope_id' && field.typeName === `Cell<Option<ScopeId>>`) {
scopeIdField = field;
}
return field.innerTypeName in types;
});
const {scopeArgs} = type;
let scopeEnterField, enterScopeCode = '', exitScopeCode = '';
if (scopeArgs && scopeIdField) {
// Get field to enter scope before
const enterFieldName = scopeArgs.enterScopeBefore;
if (enterFieldName) {
scopeEnterField = visitedFields.find(field => field.name === enterFieldName);
assert(
scopeEnterField,
`\`ast\` attr says to enter scope before field '${enterFieldName}' `
+ `in '${type.name}', but that field is not visited`
);
} else {
scopeEnterField = visitedFields[0];
}
const { scopeArgs } = type;
let scopeEnterField, enterScopeCode = '', exitScopeCode = '';
if (scopeArgs && scopeIdField) {
// Get field to enter scope before
const enterFieldName = scopeArgs.enterScopeBefore;
if (enterFieldName) {
scopeEnterField = visitedFields.find(field => field.name === enterFieldName);
assert(
scopeEnterField,
`\`ast\` attr says to enter scope before field '${enterFieldName}' ` +
`in '${type.name}', but that field is not visited`,
);
} else {
scopeEnterField = visitedFields[0];
}
// TODO: Maybe this isn't quite right. `scope_id` fields are `Cell<Option<ScopeId>>`,
// so visitor is able to alter the `scope_id` of a node from higher up the tree,
// but we don't take that into account.
// Visitor should not do that though, so maybe it's OK.
// In final version, we should not make `scope_id` fields `Cell`s to prevent this.
enterScopeCode = `
// TODO: Maybe this isn't quite right. `scope_id` fields are `Cell<Option<ScopeId>>`,
// so visitor is able to alter the `scope_id` of a node from higher up the tree,
// but we don't take that into account.
// Visitor should not do that though, so maybe it's OK.
// In final version, we should not make `scope_id` fields `Cell`s to prevent this.
enterScopeCode = `
let previous_scope_id = ctx.current_scope_id();
ctx.set_current_scope_id((*(${makeFieldCode(scopeIdField)})).get().unwrap());
`;
exitScopeCode = `ctx.set_current_scope_id(previous_scope_id);`;
}
exitScopeCode = `ctx.set_current_scope_id(previous_scope_id);`;
}
const fieldsCodes = visitedFields.map((field, index) => {
const fieldWalkName = `walk_${camelToSnake(field.innerTypeName)}`,
fieldCamelName = snakeToCamel(field.name);
const scopeCode = field === scopeEnterField ? enterScopeCode : '';
const fieldsCodes = visitedFields.map((field, index) => {
const fieldWalkName = `walk_${camelToSnake(field.innerTypeName)}`,
fieldCamelName = snakeToCamel(field.name);
const scopeCode = field === scopeEnterField ? enterScopeCode : '';
let tagCode = '', retagCode = '';
if (index === 0) {
tagCode = `
let tagCode = '', retagCode = '';
if (index === 0) {
tagCode = `
let pop_token = ctx.push_stack(
Ancestor::${type.name}${fieldCamelName}(
ancestor::${type.name}Without${fieldCamelName}(node, PhantomData)
)
);
`;
} else {
retagCode = `ctx.retag_stack(AncestorType::${type.name}${fieldCamelName});`;
}
const fieldCode = makeFieldCode(field);
if (field.wrappers[0] === 'Option') {
let walkCode;
if (field.wrappers.length === 2 && field.wrappers[1] === 'Vec') {
if (field.typeNameInner === 'Statement') {
// Special case for `Option<Vec<Statement>>`
walkCode = `walk_statements(traverser, std::ptr::from_mut(field), ctx);`;
} else {
retagCode = `ctx.retag_stack(AncestorType::${type.name}${fieldCamelName});`;
}
const fieldCode = makeFieldCode(field);
if (field.wrappers[0] === 'Option') {
let walkCode;
if (field.wrappers.length === 2 && field.wrappers[1] === 'Vec') {
if (field.typeNameInner === 'Statement') {
// Special case for `Option<Vec<Statement>>`
walkCode = `walk_statements(traverser, std::ptr::from_mut(field), ctx);`;
} else {
walkCode = `
walkCode = `
for item in field.iter_mut() {
${fieldWalkName}(traverser, std::ptr::from_mut(item), ctx);
}
`.trim();
}
} else if (field.wrappers.length === 2 && field.wrappers[1] === 'Box') {
walkCode = `${fieldWalkName}(traverser, std::ptr::from_mut(&mut **field), ctx);`;
} else {
assert(field.wrappers.length === 1, `Cannot handle struct field with type ${field.typeName}`);
walkCode = `${fieldWalkName}(traverser, std::ptr::from_mut(field), ctx);`;
}
}
} else if (field.wrappers.length === 2 && field.wrappers[1] === 'Box') {
walkCode = `${fieldWalkName}(traverser, std::ptr::from_mut(&mut **field), ctx);`;
} else {
assert(field.wrappers.length === 1, `Cannot handle struct field with type ${field.typeName}`);
walkCode = `${fieldWalkName}(traverser, std::ptr::from_mut(field), ctx);`;
}
return `
return `
${scopeCode}
${tagCode}
if let Some(field) = &mut *(${fieldCode}) {
@@ -131,59 +131,59 @@ function generateWalkForStruct(type, types) {
${walkCode}
}
`;
}
}
if (field.wrappers[0] === 'Vec') {
let walkVecCode;
if (field.wrappers.length === 1 && field.innerTypeName === 'Statement') {
// Special case for `Vec<Statement>`
walkVecCode = `walk_statements(traverser, ${fieldCode}, ctx);`
} else {
let walkCode = `${fieldWalkName}(traverser, std::ptr::from_mut(item), ctx);`,
iterModifier = '';
if (field.wrappers.length === 2 && field.wrappers[1] === 'Option') {
iterModifier = '.flatten()';
} else {
assert(
field.wrappers.length === 1,
`Cannot handle struct field with type ${field.type}`
);
}
walkVecCode = `
if (field.wrappers[0] === 'Vec') {
let walkVecCode;
if (field.wrappers.length === 1 && field.innerTypeName === 'Statement') {
// Special case for `Vec<Statement>`
walkVecCode = `walk_statements(traverser, ${fieldCode}, ctx);`;
} else {
let walkCode = `${fieldWalkName}(traverser, std::ptr::from_mut(item), ctx);`,
iterModifier = '';
if (field.wrappers.length === 2 && field.wrappers[1] === 'Option') {
iterModifier = '.flatten()';
} else {
assert(
field.wrappers.length === 1,
`Cannot handle struct field with type ${field.type}`,
);
}
walkVecCode = `
for item in (*(${fieldCode})).iter_mut()${iterModifier} {
${walkCode}
}
`.trim();
}
}
return `
return `
${scopeCode}
${tagCode || retagCode}
${walkVecCode}
`;
}
}
if (field.wrappers.length === 1 && field.wrappers[0] === 'Box') {
return `
if (field.wrappers.length === 1 && field.wrappers[0] === 'Box') {
return `
${scopeCode}
${tagCode || retagCode}
${fieldWalkName}(traverser, std::ptr::from_mut(&mut **(${fieldCode})), ctx);
`;
}
}
assert(field.wrappers.length === 0, `Cannot handle struct field with type: ${field.type}`);
assert(field.wrappers.length === 0, `Cannot handle struct field with type: ${field.type}`);
return `
return `
${scopeCode}
${tagCode || retagCode}
${fieldWalkName}(traverser, ${fieldCode}, ctx);
`;
});
});
if (visitedFields.length > 0) fieldsCodes.push('ctx.pop_stack(pop_token);');
if (visitedFields.length > 0) fieldsCodes.push('ctx.pop_stack(pop_token);');
const typeSnakeName = camelToSnake(type.name);
return `
const typeSnakeName = camelToSnake(type.name);
return `
pub(crate) unsafe fn walk_${typeSnakeName}<'a, Tr: Traverse<'a>>(
traverser: &mut Tr,
node: *mut ${type.rawName},
@@ -198,53 +198,53 @@ function generateWalkForStruct(type, types) {
}
function makeFieldCode(field) {
return `node.cast::<u8>().add(ancestor::${field.offsetVarName}).cast::<${field.typeName}>()`;
return `node.cast::<u8>().add(ancestor::${field.offsetVarName}).cast::<${field.typeName}>()`;
}
function generateWalkForEnum(type, types) {
const variantCodes = type.variants.map((variant) => {
const variantType = types[variant.innerTypeName];
assert(variantType, `Cannot handle enum variant with type: ${variant.type}`);
const variantCodes = type.variants.map((variant) => {
const variantType = types[variant.innerTypeName];
assert(variantType, `Cannot handle enum variant with type: ${variant.type}`);
let nodeCode = '(node)';
if (variant.wrappers.length === 1 && variant.wrappers[0] === 'Box') {
nodeCode = '(&mut **node)';
} else {
assert(variant.wrappers.length === 0, `Cannot handle enum variant with type: ${variant.type}`);
}
return `${type.name}::${variant.name}(node) => `
+ `walk_${camelToSnake(variant.innerTypeName)}(traverser, std::ptr::from_mut${nodeCode}, ctx),`;
});
const missingVariants = [];
for (const inheritedTypeName of type.inherits) {
// Recurse into nested inherited types
const variantMatches = [],
inheritedFrom = [inheritedTypeName];
for (let i = 0; i < inheritedFrom.length; i++) {
const inheritedTypeName = inheritedFrom[i],
inheritedType = types[inheritedTypeName];
if (!inheritedType || inheritedType.kind !== 'enum') {
missingVariants.push(inheritedTypeName);
} else {
variantMatches.push(...inheritedType.variants.map(
variant => `${type.name}::${variant.name}(_)`
));
inheritedFrom.push(...inheritedType.inherits);
}
}
variantCodes.push(
`${variantMatches.join(' | ')} => `
+ `walk_${camelToSnake(inheritedTypeName)}(traverser, node.cast(), ctx),`
);
let nodeCode = '(node)';
if (variant.wrappers.length === 1 && variant.wrappers[0] === 'Box') {
nodeCode = '(&mut **node)';
} else {
assert(variant.wrappers.length === 0, `Cannot handle enum variant with type: ${variant.type}`);
}
assert(missingVariants.length === 0, `Missing enum variants: ${missingVariants.join(', ')}`);
return `${type.name}::${variant.name}(node) => ` +
`walk_${camelToSnake(variant.innerTypeName)}(traverser, std::ptr::from_mut${nodeCode}, ctx),`;
});
const typeSnakeName = camelToSnake(type.name);
return `
const missingVariants = [];
for (const inheritedTypeName of type.inherits) {
// Recurse into nested inherited types
const variantMatches = [],
inheritedFrom = [inheritedTypeName];
for (let i = 0; i < inheritedFrom.length; i++) {
const inheritedTypeName = inheritedFrom[i],
inheritedType = types[inheritedTypeName];
if (!inheritedType || inheritedType.kind !== 'enum') {
missingVariants.push(inheritedTypeName);
} else {
variantMatches.push(...inheritedType.variants.map(
variant => `${type.name}::${variant.name}(_)`,
));
inheritedFrom.push(...inheritedType.inherits);
}
}
variantCodes.push(
`${variantMatches.join(' | ')} => ` +
`walk_${camelToSnake(inheritedTypeName)}(traverser, node.cast(), ctx),`,
);
}
assert(missingVariants.length === 0, `Missing enum variants: ${missingVariants.join(', ')}`);
const typeSnakeName = camelToSnake(type.name);
return `
pub(crate) unsafe fn walk_${typeSnakeName}<'a, Tr: Traverse<'a>>(
traverser: &mut Tr,
node: *mut ${type.rawName},
+1 -3
View File
@@ -11,10 +11,8 @@
"tasks/coverage/misc",
"**/tests/**",
"**/fixtures/**",
"**/*.snap",
"**/*.snap.*",
"**/CHANGELOG.md",
"~"
"**/CHANGELOG.md"
],
"plugins": [
"https://plugins.dprint.dev/typescript-0.91.8.wasm",
+42 -50
View File
@@ -1,34 +1,29 @@
import {
ExtensionContext,
window,
commands,
workspace,
StatusBarItem,
StatusBarAlignment,
ConfigurationTarget,
ExtensionContext,
StatusBarAlignment,
StatusBarItem,
ThemeColor,
} from "vscode";
window,
workspace,
} from 'vscode';
import {
Executable,
LanguageClient,
LanguageClientOptions,
ServerOptions,
} from "vscode-languageclient/node";
import { Executable, LanguageClient, LanguageClientOptions, ServerOptions } from 'vscode-languageclient/node';
import { join } from "node:path";
import { join } from 'node:path';
const languageClientId = "oxc-vscode";
const languageClientName = "oxc";
const outputChannelName = "oxc_language_server";
const traceOutputChannelName = "oxc_language_server.trace";
const languageClientId = 'oxc-vscode';
const languageClientName = 'oxc';
const outputChannelName = 'oxc_language_server';
const traceOutputChannelName = 'oxc_language_server.trace';
const enum OxcCommands {
RestartServer = "oxc.restartServer",
ApplyAllFixes = "oxc.applyAllFixes",
ShowOutputChannel = "oxc.showOutputChannel",
ShowTraceOutputChannel = "oxc.showTraceOutputChannel",
ToggleEnable = "oxc.toggleEnable",
RestartServer = 'oxc.restartServer',
ApplyAllFixes = 'oxc.applyAllFixes',
ShowOutputChannel = 'oxc.showOutputChannel',
ShowTraceOutputChannel = 'oxc.showTraceOutputChannel',
ToggleEnable = 'oxc.toggleEnable',
}
let client: LanguageClient;
@@ -40,7 +35,7 @@ export async function activate(context: ExtensionContext) {
OxcCommands.RestartServer,
async () => {
if (!client) {
window.showErrorMessage("oxc client not found");
window.showErrorMessage('oxc client not found');
return;
}
@@ -48,12 +43,12 @@ export async function activate(context: ExtensionContext) {
if (client.isRunning()) {
await client.restart();
window.showInformationMessage("oxc server restarted.");
window.showInformationMessage('oxc server restarted.');
} else {
await client.start();
}
} catch (err) {
client.error("Restarting client failed", err, "force");
client.error('Restarting client failed', err, 'force');
}
},
);
@@ -76,12 +71,12 @@ export async function activate(context: ExtensionContext) {
OxcCommands.ToggleEnable,
() => {
let enabled = workspace
.getConfiguration("oxc_language_server")
.get("enable");
.getConfiguration('oxc_language_server')
.get('enable');
let nextState = !enabled;
workspace
.getConfiguration("oxc_language_server")
.update("enable", nextState, ConfigurationTarget.Global);
.getConfiguration('oxc_language_server')
.update('enable', nextState, ConfigurationTarget.Global);
},
);
@@ -95,17 +90,16 @@ export async function activate(context: ExtensionContext) {
const outputChannel = window.createOutputChannel(outputChannelName);
const traceOutputChannel = window.createOutputChannel(traceOutputChannelName);
const ext = process.platform === "win32" ? ".exe" : "";
const ext = process.platform === 'win32' ? '.exe' : '';
// NOTE: The `./target/release` path is aligned with the path defined in .github/workflows/release_vscode.yml
const command =
process.env.SERVER_PATH_DEV ??
const command = process.env.SERVER_PATH_DEV ??
join(context.extensionPath, `./target/release/oxc_language_server${ext}`);
const run: Executable = {
command: command!,
options: {
env: {
...process.env,
RUST_LOG: process.env.RUST_LOG || "info",
RUST_LOG: process.env.RUST_LOG || 'info',
},
},
};
@@ -117,24 +111,24 @@ export async function activate(context: ExtensionContext) {
// Otherwise the run options are used
// Options to control the language client
let clientConfig: any = JSON.parse(
JSON.stringify(workspace.getConfiguration("oxc_language_server")),
JSON.stringify(workspace.getConfiguration('oxc_language_server')),
);
let clientOptions: LanguageClientOptions = {
// Register the server for plain text documents
documentSelector: [
"typescript",
"javascript",
"typescriptreact",
"javascriptreact",
"vue",
"svelte",
'typescript',
'javascript',
'typescriptreact',
'javascriptreact',
'vue',
'svelte',
].map((lang) => ({
language: lang,
scheme: "file",
scheme: 'file',
})),
synchronize: {
// Notify the server about file changes to '.clientrc files contained in the workspace
fileEvents: workspace.createFileSystemWatcher("**/.clientrc"),
fileEvents: workspace.createFileSystemWatcher('**/.clientrc'),
},
initializationOptions: {
settings: clientConfig,
@@ -151,15 +145,15 @@ export async function activate(context: ExtensionContext) {
clientOptions,
);
workspace.onDidChangeConfiguration((e) => {
let isAffected = e.affectsConfiguration("oxc_language_server");
let isAffected = e.affectsConfiguration('oxc_language_server');
if (!isAffected) {
return;
}
let settings: any = JSON.parse(
JSON.stringify(workspace.getConfiguration("oxc_language_server")),
JSON.stringify(workspace.getConfiguration('oxc_language_server')),
);
updateStatsBar(settings.enable);
client.sendNotification("workspace/didChangeConfiguration", { settings });
client.sendNotification('workspace/didChangeConfiguration', { settings });
});
function updateStatsBar(enable: boolean) {
@@ -174,12 +168,10 @@ export async function activate(context: ExtensionContext) {
}
let bgColor = new ThemeColor(
enable
? "statusBarItem.activeBackground"
: "statusBarItem.errorBackground",
? 'statusBarItem.activeBackground'
: 'statusBarItem.errorBackground',
);
myStatusBarItem.text = `oxc: ${
enable ? "$(check-all)" : "$(circle-slash)"
}`;
myStatusBarItem.text = `oxc: ${enable ? '$(check-all)' : '$(circle-slash)'}`;
myStatusBarItem.backgroundColor = bgColor;
}
+1
View File
@@ -63,6 +63,7 @@ ast:
fmt:
cargo fmt --all
taplo format
dprint fmt
# Run cargo check
check:
+38 -33
View File
@@ -1,25 +1,25 @@
/* auto-generated by NAPI-RS */
/* eslint-disable */
export interface Comment {
type: 'Line' | 'Block'
value: string
start: number
end: number
type: 'Line' | 'Block';
value: string;
start: number;
end: number;
}
export interface ExportSpecifier {
/** Exported name */
n: string
n: string;
/** Local name, or undefined. */
ln?: string
ln?: string;
/** Start of exported name */
s: number
s: number;
/** End of exported name */
e: number
e: number;
/** Start of local name */
ls?: number
ls?: number;
/** End of local name */
le?: number
le?: number;
}
export interface ImportSpecifier {
@@ -30,40 +30,40 @@ export interface ImportSpecifier {
*
* For dynamic import expressions, this field will be empty if not a valid JS string.
*/
n?: string
n?: string;
/** Start of module specifier */
s: number
s: number;
/** End of module specifier */
e: number
e: number;
/** Start of import statement */
ss: number
ss: number;
/** End of import statement */
se: number
se: number;
/**
* Import Type
* * If this import keyword is a dynamic import, this is the start value.
* * If this import keyword is a static import, this is -1.
* * If this import keyword is an import.meta expression, this is -2.
*/
d: number
d: number;
/**
* If this import has an import assertion, this is the start value
* Otherwise this is `-1`.
*/
a: number
a: number;
}
export interface ModuleLexer {
imports: Array<ImportSpecifier>
exports: Array<ExportSpecifier>
imports: Array<ImportSpecifier>;
exports: Array<ExportSpecifier>;
/**
* ESM syntax detection
*
* The use of ESM syntax: import / export statements and `import.meta`
*/
hasModuleSyntax: boolean
hasModuleSyntax: boolean;
/** Facade modules that only use import / export syntax */
facade: boolean
facade: boolean;
}
/**
@@ -71,7 +71,10 @@ export interface ModuleLexer {
*
* * Tokio crashes
*/
export declare function moduleLexerAsync(sourceText: string, options?: ParserOptions | undefined | null): Promise<ModuleLexer>
export declare function moduleLexerAsync(
sourceText: string,
options?: ParserOptions | undefined | null,
): Promise<ModuleLexer>;
/**
* Outputs the list of exports and locations of import specifiers,
@@ -81,19 +84,22 @@ export declare function moduleLexerAsync(sourceText: string, options?: ParserOpt
*
* * File extension is invalid
*/
export declare function moduleLexerSync(sourceText: string, options?: ParserOptions | undefined | null): ModuleLexer
export declare function moduleLexerSync(sourceText: string, options?: ParserOptions | undefined | null): ModuleLexer;
/**
* # Panics
*
* * Tokio crashes
*/
export declare function parseAsync(sourceText: string, options?: ParserOptions | undefined | null): Promise<ParseResult>
export declare function parseAsync(
sourceText: string,
options?: ParserOptions | undefined | null,
): Promise<ParseResult>;
export interface ParseResult {
program: string
comments: Array<Comment>
errors: Array<string>
program: string;
comments: Array<Comment>;
errors: Array<string>;
}
/**
@@ -102,8 +108,8 @@ export interface ParseResult {
* <https://github.com/babel/babel/blob/main/packages/babel-parser/typings/babel-parser.d.ts>
*/
export interface ParserOptions {
sourceType?: 'script' | 'module' | 'unambiguous' | undefined
sourceFilename?: string
sourceType?: 'script' | 'module' | 'unambiguous' | undefined;
sourceFilename?: string;
/**
* Emit `ParenthesizedExpression` in AST.
*
@@ -113,7 +119,7 @@ export interface ParserOptions {
*
* Default: true
*/
preserveParens?: boolean
preserveParens?: boolean;
}
/**
@@ -122,7 +128,7 @@ export interface ParserOptions {
* * File extension is invalid
* * Serde JSON serialization
*/
export declare function parseSync(sourceText: string, options?: ParserOptions | undefined | null): ParseResult
export declare function parseSync(sourceText: string, options?: ParserOptions | undefined | null): ParseResult;
/**
* Parse without returning anything.
@@ -133,5 +139,4 @@ export declare function parseSync(sourceText: string, options?: ParserOptions |
* * File extension is invalid
* * Serde JSON serialization
*/
export declare function parseWithoutReturn(sourceText: string, options?: ParserOptions | undefined | null): void
export declare function parseWithoutReturn(sourceText: string, options?: ParserOptions | undefined | null): void;
+165 -184
View File
@@ -2,349 +2,330 @@
/* eslint-disable */
/* auto-generated by NAPI-RS */
const { readFileSync } = require('fs')
const { readFileSync } = require('fs');
let nativeBinding = null
const loadErrors = []
let nativeBinding = null;
const loadErrors = [];
const isMusl = () => {
let musl = false
let musl = false;
if (process.platform === 'linux') {
musl = isMuslFromFilesystem()
musl = isMuslFromFilesystem();
if (musl === null) {
musl = isMuslFromReport()
musl = isMuslFromReport();
}
if (musl === null) {
musl = isMuslFromChildProcess()
musl = isMuslFromChildProcess();
}
}
return musl
}
return musl;
};
const isFileMusl = (f) => f.includes('libc.musl-') || f.includes('ld-musl-')
const isFileMusl = (f) => f.includes('libc.musl-') || f.includes('ld-musl-');
const isMuslFromFilesystem = () => {
try {
return readFileSync('/usr/bin/ldd', 'utf-8').includes('musl')
return readFileSync('/usr/bin/ldd', 'utf-8').includes('musl');
} catch {
return null
return null;
}
}
};
const isMuslFromReport = () => {
const report = typeof process.report.getReport === 'function' ? process.report.getReport() : null
const report = typeof process.report.getReport === 'function' ? process.report.getReport() : null;
if (!report) {
return null
return null;
}
if (report.header && report.header.glibcVersionRuntime) {
return false
return false;
}
if (Array.isArray(report.sharedObjects)) {
if (report.sharedObjects.some(isFileMusl)) {
return true
return true;
}
}
return false
}
return false;
};
const isMuslFromChildProcess = () => {
try {
return require('child_process').execSync('ldd --version', { encoding: 'utf8' }).includes('musl')
return require('child_process').execSync('ldd --version', { encoding: 'utf8' }).includes('musl');
} catch (e) {
// If we reach this case, we don't know if the system is musl or not, so is better to just fallback to false
return false
return false;
}
}
};
function requireNative() {
if (process.platform === 'android') {
if (process.arch === 'arm64') {
try {
return require('./parser.android-arm64.node')
return require('./parser.android-arm64.node');
} catch (e) {
loadErrors.push(e)
loadErrors.push(e);
}
try {
return require('@oxc-parser/binding-android-arm64')
return require('@oxc-parser/binding-android-arm64');
} catch (e) {
loadErrors.push(e)
loadErrors.push(e);
}
} else if (process.arch === 'arm') {
try {
return require('./parser.android-arm-eabi.node')
return require('./parser.android-arm-eabi.node');
} catch (e) {
loadErrors.push(e)
loadErrors.push(e);
}
try {
return require('@oxc-parser/binding-android-arm-eabi')
return require('@oxc-parser/binding-android-arm-eabi');
} catch (e) {
loadErrors.push(e)
loadErrors.push(e);
}
} else {
loadErrors.push(new Error(`Unsupported architecture on Android ${process.arch}`))
loadErrors.push(new Error(`Unsupported architecture on Android ${process.arch}`));
}
} else if (process.platform === 'win32') {
if (process.arch === 'x64') {
try {
return require('./parser.win32-x64-msvc.node')
return require('./parser.win32-x64-msvc.node');
} catch (e) {
loadErrors.push(e)
loadErrors.push(e);
}
try {
return require('@oxc-parser/binding-win32-x64-msvc')
return require('@oxc-parser/binding-win32-x64-msvc');
} catch (e) {
loadErrors.push(e)
loadErrors.push(e);
}
} else if (process.arch === 'ia32') {
try {
return require('./parser.win32-ia32-msvc.node')
return require('./parser.win32-ia32-msvc.node');
} catch (e) {
loadErrors.push(e)
loadErrors.push(e);
}
try {
return require('@oxc-parser/binding-win32-ia32-msvc')
return require('@oxc-parser/binding-win32-ia32-msvc');
} catch (e) {
loadErrors.push(e)
loadErrors.push(e);
}
} else if (process.arch === 'arm64') {
try {
return require('./parser.win32-arm64-msvc.node')
return require('./parser.win32-arm64-msvc.node');
} catch (e) {
loadErrors.push(e)
loadErrors.push(e);
}
try {
return require('@oxc-parser/binding-win32-arm64-msvc')
return require('@oxc-parser/binding-win32-arm64-msvc');
} catch (e) {
loadErrors.push(e)
loadErrors.push(e);
}
} else {
loadErrors.push(new Error(`Unsupported architecture on Windows: ${process.arch}`))
loadErrors.push(new Error(`Unsupported architecture on Windows: ${process.arch}`));
}
} else if (process.platform === 'darwin') {
try {
return require('./parser.darwin-universal.node')
} catch (e) {
loadErrors.push(e)
}
try {
return require('@oxc-parser/binding-darwin-universal')
} catch (e) {
loadErrors.push(e)
}
return require('./parser.darwin-universal.node');
} catch (e) {
loadErrors.push(e);
}
try {
return require('@oxc-parser/binding-darwin-universal');
} catch (e) {
loadErrors.push(e);
}
if (process.arch === 'x64') {
try {
return require('./parser.darwin-x64.node')
return require('./parser.darwin-x64.node');
} catch (e) {
loadErrors.push(e)
loadErrors.push(e);
}
try {
return require('@oxc-parser/binding-darwin-x64')
return require('@oxc-parser/binding-darwin-x64');
} catch (e) {
loadErrors.push(e)
loadErrors.push(e);
}
} else if (process.arch === 'arm64') {
try {
return require('./parser.darwin-arm64.node')
return require('./parser.darwin-arm64.node');
} catch (e) {
loadErrors.push(e)
loadErrors.push(e);
}
try {
return require('@oxc-parser/binding-darwin-arm64')
return require('@oxc-parser/binding-darwin-arm64');
} catch (e) {
loadErrors.push(e)
loadErrors.push(e);
}
} else {
loadErrors.push(new Error(`Unsupported architecture on macOS: ${process.arch}`))
loadErrors.push(new Error(`Unsupported architecture on macOS: ${process.arch}`));
}
} else if (process.platform === 'freebsd') {
if (process.arch === 'x64') {
try {
return require('./parser.freebsd-x64.node')
return require('./parser.freebsd-x64.node');
} catch (e) {
loadErrors.push(e)
loadErrors.push(e);
}
try {
return require('@oxc-parser/binding-freebsd-x64')
return require('@oxc-parser/binding-freebsd-x64');
} catch (e) {
loadErrors.push(e)
loadErrors.push(e);
}
} else if (process.arch === 'arm64') {
try {
return require('./parser.freebsd-arm64.node')
return require('./parser.freebsd-arm64.node');
} catch (e) {
loadErrors.push(e)
loadErrors.push(e);
}
try {
return require('@oxc-parser/binding-freebsd-arm64')
return require('@oxc-parser/binding-freebsd-arm64');
} catch (e) {
loadErrors.push(e)
loadErrors.push(e);
}
} else {
loadErrors.push(new Error(`Unsupported architecture on FreeBSD: ${process.arch}`))
loadErrors.push(new Error(`Unsupported architecture on FreeBSD: ${process.arch}`));
}
} else if (process.platform === 'linux') {
if (process.arch === 'x64') {
if (isMusl()) {
try {
return require('./parser.linux-x64-musl.node')
} catch (e) {
loadErrors.push(e)
}
try {
return require('@oxc-parser/binding-linux-x64-musl')
} catch (e) {
loadErrors.push(e)
}
return require('./parser.linux-x64-musl.node');
} catch (e) {
loadErrors.push(e);
}
try {
return require('@oxc-parser/binding-linux-x64-musl');
} catch (e) {
loadErrors.push(e);
}
} else {
try {
return require('./parser.linux-x64-gnu.node')
} catch (e) {
loadErrors.push(e)
}
try {
return require('@oxc-parser/binding-linux-x64-gnu')
} catch (e) {
loadErrors.push(e)
}
return require('./parser.linux-x64-gnu.node');
} catch (e) {
loadErrors.push(e);
}
try {
return require('@oxc-parser/binding-linux-x64-gnu');
} catch (e) {
loadErrors.push(e);
}
}
} else if (process.arch === 'arm64') {
if (isMusl()) {
try {
return require('./parser.linux-arm64-musl.node')
} catch (e) {
loadErrors.push(e)
}
try {
return require('@oxc-parser/binding-linux-arm64-musl')
} catch (e) {
loadErrors.push(e)
}
return require('./parser.linux-arm64-musl.node');
} catch (e) {
loadErrors.push(e);
}
try {
return require('@oxc-parser/binding-linux-arm64-musl');
} catch (e) {
loadErrors.push(e);
}
} else {
try {
return require('./parser.linux-arm64-gnu.node')
} catch (e) {
loadErrors.push(e)
}
try {
return require('@oxc-parser/binding-linux-arm64-gnu')
} catch (e) {
loadErrors.push(e)
}
return require('./parser.linux-arm64-gnu.node');
} catch (e) {
loadErrors.push(e);
}
try {
return require('@oxc-parser/binding-linux-arm64-gnu');
} catch (e) {
loadErrors.push(e);
}
}
} else if (process.arch === 'arm') {
if (isMusl()) {
try {
return require('./parser.linux-arm-musleabihf.node')
} catch (e) {
loadErrors.push(e)
}
try {
return require('@oxc-parser/binding-linux-arm-musleabihf')
} catch (e) {
loadErrors.push(e)
}
return require('./parser.linux-arm-musleabihf.node');
} catch (e) {
loadErrors.push(e);
}
try {
return require('@oxc-parser/binding-linux-arm-musleabihf');
} catch (e) {
loadErrors.push(e);
}
} else {
try {
return require('./parser.linux-arm-gnueabihf.node')
} catch (e) {
loadErrors.push(e)
}
try {
return require('@oxc-parser/binding-linux-arm-gnueabihf')
} catch (e) {
loadErrors.push(e)
}
return require('./parser.linux-arm-gnueabihf.node');
} catch (e) {
loadErrors.push(e);
}
try {
return require('@oxc-parser/binding-linux-arm-gnueabihf');
} catch (e) {
loadErrors.push(e);
}
}
} else if (process.arch === 'riscv64') {
if (isMusl()) {
try {
return require('./parser.linux-riscv64-musl.node')
} catch (e) {
loadErrors.push(e)
}
try {
return require('@oxc-parser/binding-linux-riscv64-musl')
} catch (e) {
loadErrors.push(e)
}
return require('./parser.linux-riscv64-musl.node');
} catch (e) {
loadErrors.push(e);
}
try {
return require('@oxc-parser/binding-linux-riscv64-musl');
} catch (e) {
loadErrors.push(e);
}
} else {
try {
return require('./parser.linux-riscv64-gnu.node')
} catch (e) {
loadErrors.push(e)
}
try {
return require('@oxc-parser/binding-linux-riscv64-gnu')
} catch (e) {
loadErrors.push(e)
}
return require('./parser.linux-riscv64-gnu.node');
} catch (e) {
loadErrors.push(e);
}
try {
return require('@oxc-parser/binding-linux-riscv64-gnu');
} catch (e) {
loadErrors.push(e);
}
}
} else if (process.arch === 'ppc64') {
try {
return require('./parser.linux-ppc64-gnu.node')
return require('./parser.linux-ppc64-gnu.node');
} catch (e) {
loadErrors.push(e)
loadErrors.push(e);
}
try {
return require('@oxc-parser/binding-linux-ppc64-gnu')
return require('@oxc-parser/binding-linux-ppc64-gnu');
} catch (e) {
loadErrors.push(e)
loadErrors.push(e);
}
} else if (process.arch === 's390x') {
try {
return require('./parser.linux-s390x-gnu.node')
return require('./parser.linux-s390x-gnu.node');
} catch (e) {
loadErrors.push(e)
loadErrors.push(e);
}
try {
return require('@oxc-parser/binding-linux-s390x-gnu')
return require('@oxc-parser/binding-linux-s390x-gnu');
} catch (e) {
loadErrors.push(e)
loadErrors.push(e);
}
} else {
loadErrors.push(new Error(`Unsupported architecture on Linux: ${process.arch}`))
loadErrors.push(new Error(`Unsupported architecture on Linux: ${process.arch}`));
}
} else {
loadErrors.push(new Error(`Unsupported OS: ${process.platform}, architecture: ${process.arch}`))
loadErrors.push(new Error(`Unsupported OS: ${process.platform}, architecture: ${process.arch}`));
}
}
nativeBinding = requireNative()
nativeBinding = requireNative();
if (!nativeBinding || process.env.NAPI_RS_FORCE_WASI) {
try {
nativeBinding = require('./parser.wasi.cjs')
nativeBinding = require('./parser.wasi.cjs');
} catch (err) {
if (process.env.NAPI_RS_FORCE_WASI) {
console.error(err)
console.error(err);
}
}
if (!nativeBinding) {
try {
nativeBinding = require('@oxc-parser/binding-wasm32-wasi')
nativeBinding = require('@oxc-parser/binding-wasm32-wasi');
} catch (err) {
if (process.env.NAPI_RS_FORCE_WASI) {
console.error(err)
console.error(err);
}
}
}
@@ -356,13 +337,13 @@ if (!nativeBinding) {
// - The package owner could build/publish bindings for this arch
// - The user may need to bundle the correct files
// - The user may need to re-install node_modules to get new packages
throw new Error('Failed to load native binding', { cause: loadErrors })
throw new Error('Failed to load native binding', { cause: loadErrors });
}
throw new Error(`Failed to load native binding`)
throw new Error(`Failed to load native binding`);
}
module.exports.moduleLexerAsync = nativeBinding.moduleLexerAsync
module.exports.moduleLexerSync = nativeBinding.moduleLexerSync
module.exports.parseAsync = nativeBinding.parseAsync
module.exports.parseSync = nativeBinding.parseSync
module.exports.parseWithoutReturn = nativeBinding.parseWithoutReturn
module.exports.moduleLexerAsync = nativeBinding.moduleLexerAsync;
module.exports.moduleLexerSync = nativeBinding.moduleLexerSync;
module.exports.parseAsync = nativeBinding.parseAsync;
module.exports.parseSync = nativeBinding.parseSync;
module.exports.parseWithoutReturn = nativeBinding.parseWithoutReturn;
+45 -45
View File
@@ -1,61 +1,61 @@
import {fileURLToPath} from 'url';
import {join as pathJoin} from 'path';
import {readFile, writeFile} from 'fs/promises';
import {Bench} from 'tinybench';
import {parseSync} from './index.js';
import { readFile, writeFile } from 'fs/promises';
import { join as pathJoin } from 'path';
import { Bench } from 'tinybench';
import { fileURLToPath } from 'url';
import { parseSync } from './index.js';
const IS_CI = !!process.env.CI,
ACCURATE = IS_CI || process.env.ACCURATE;
ACCURATE = IS_CI || process.env.ACCURATE;
const urls = [
// TypeScript syntax (2.81MB)
'https://raw.githubusercontent.com/microsoft/TypeScript/v5.3.3/src/compiler/checker.ts',
// Real world app tsx (1.0M)
'https://raw.githubusercontent.com/oxc-project/benchmark-files/main/cal.com.tsx',
// Real world content-heavy app jsx (3K)
'https://raw.githubusercontent.com/oxc-project/benchmark-files/main/RadixUIAdoptionSection.jsx',
// Heavy with classes (554K)
'https://cdn.jsdelivr.net/npm/pdfjs-dist@4.0.269/build/pdf.mjs',
// ES5 (3.9M)
'https://cdn.jsdelivr.net/npm/antd@5.12.5/dist/antd.js',
// TypeScript syntax (2.81MB)
'https://raw.githubusercontent.com/microsoft/TypeScript/v5.3.3/src/compiler/checker.ts',
// Real world app tsx (1.0M)
'https://raw.githubusercontent.com/oxc-project/benchmark-files/main/cal.com.tsx',
// Real world content-heavy app jsx (3K)
'https://raw.githubusercontent.com/oxc-project/benchmark-files/main/RadixUIAdoptionSection.jsx',
// Heavy with classes (554K)
'https://cdn.jsdelivr.net/npm/pdfjs-dist@4.0.269/build/pdf.mjs',
// ES5 (3.9M)
'https://cdn.jsdelivr.net/npm/antd@5.12.5/dist/antd.js',
];
// Same directory as Rust benchmarks use for downloaded files
const cacheDirPath = pathJoin(fileURLToPath(import.meta.url), '../../../target');
const files = await Promise.all(urls.map(async (url) => {
const filename = url.split('/').at(-1),
path = pathJoin(cacheDirPath, filename);
const filename = url.split('/').at(-1),
path = pathJoin(cacheDirPath, filename);
let code;
try {
code = await readFile(path, 'utf8');
if (IS_CI) console.log('Found cached file:', filename);
} catch {
if (IS_CI) console.log('Downloading:', filename);
const res = await fetch(url);
code = await res.text();
await writeFile(path, code);
}
let code;
try {
code = await readFile(path, 'utf8');
if (IS_CI) console.log('Found cached file:', filename);
} catch {
if (IS_CI) console.log('Downloading:', filename);
const res = await fetch(url);
code = await res.text();
await writeFile(path, code);
}
return {filename, code};
return { filename, code };
}));
const bench = new Bench(
ACCURATE
ACCURATE
? {
warmupIterations: 20, // Default is 5
time: 5000, // 5 seconds, default is 500 ms
iterations: 100, // Default is 10
warmupIterations: 20, // Default is 5
time: 5000, // 5 seconds, default is 500 ms
iterations: 100, // Default is 10
}
: undefined
: undefined,
);
for (const {filename, code} of files) {
bench.add(`parser_napi[${filename}]`, () => {
const res = parseSync(code, {sourceFilename: filename});
JSON.parse(res.program);
});
for (const { filename, code } of files) {
bench.add(`parser_napi[${filename}]`, () => {
const res = parseSync(code, { sourceFilename: filename });
JSON.parse(res.program);
});
}
console.log('Warming up');
@@ -66,10 +66,10 @@ console.table(bench.table());
// If running on CI, save results to file
if (IS_CI) {
const dataDir = process.env.DATA_DIR;
const results = bench.tasks.map(task => ({
filename: task.name.match(/\[(.+)\]$/)[1],
duration: task.result.period / 1000, // In seconds
}));
await writeFile(pathJoin(dataDir, 'results.json'), JSON.stringify(results));
const dataDir = process.env.DATA_DIR;
const results = bench.tasks.map(task => ({
filename: task.name.match(/\[(.+)\]$/)[1],
duration: task.result.period / 1000, // In seconds
}));
await writeFile(pathJoin(dataDir, 'results.json'), JSON.stringify(results));
}
+4 -4
View File
@@ -1,7 +1,7 @@
import oxc from './index.js';
import assert from 'assert';
import oxc from './index.js';
console.log(`Testing on ${process.platform}-${process.arch}`)
console.log(`Testing on ${process.platform}-${process.arch}`);
function test(ret) {
console.log(ret);
@@ -10,7 +10,7 @@ function test(ret) {
assert(ret.comments.length == 1);
}
const sourceText = "/* comment */ foo";
const sourceText = '/* comment */ foo';
test(oxc.parseSync(sourceText));
@@ -18,4 +18,4 @@ async function main() {
test(await oxc.parseAsync(sourceText));
}
main()
main();
+14 -15
View File
@@ -1,13 +1,13 @@
/*
* Test and bench against `es-module-lexer`
*/
import fs from "fs";
import assert from "assert";
import assert from 'assert';
import fs from 'fs';
import oxc from "./index.js";
import * as esModuleLexer from "es-module-lexer";
import * as esModuleLexer from 'es-module-lexer';
import oxc from './index.js';
const root = "/Users/boshen/github/es-module-lexer/test/samples";
const root = '/Users/boshen/github/es-module-lexer/test/samples';
main();
@@ -16,7 +16,7 @@ async function main() {
const files = fs
.readdirSync(root)
.filter((x) => x.endsWith(".js"))
.filter((x) => x.endsWith('.js'))
.map((file) => {
const source = fs.readFileSync(`${root}/${file}`);
return {
@@ -27,8 +27,7 @@ async function main() {
});
function test(source) {
const [imports, exports, facade, hasModuleSyntax] =
esModuleLexer.parse(source);
const [imports, exports, facade, hasModuleSyntax] = esModuleLexer.parse(source);
const moduleLexer = oxc.moduleLexerSync(source);
assert(moduleLexer.imports.length == imports.length);
assert(moduleLexer.exports.length == exports.length);
@@ -46,11 +45,11 @@ async function main() {
const n = 25;
bench("es-module-lexer", esModuleLexer.parse);
bench("oxc", oxc.moduleLexerSync);
bench('es-module-lexer', esModuleLexer.parse);
bench('oxc', oxc.moduleLexerSync);
function bench(name, fn) {
console.log("-----------");
console.log('-----------');
console.log(name);
doRun();
@@ -63,7 +62,7 @@ async function main() {
}
function doRun() {
console.log("Cold Run, All Samples");
console.log('Cold Run, All Samples');
let totalSize = 0;
{
let total = 0;
@@ -72,7 +71,7 @@ async function main() {
total += timeRun(code);
});
console.log(`test/samples/*.js (${Math.round(totalSize / 1e3)} KiB)`);
console.log(`> ${total + "ms"}`);
console.log(`> ${total + 'ms'}`);
}
console.log(`\nWarm Runs (average of ${n} runs)`);
@@ -84,7 +83,7 @@ async function main() {
total += timeRun(code);
}
console.log(`> ${total / n + "ms"}`);
console.log(`> ${total / n + 'ms'}`);
});
console.log(`\nWarm Runs, All Samples (average of ${n} runs)`);
@@ -96,7 +95,7 @@ async function main() {
});
}
console.log(`test/samples/*.js (${Math.round(totalSize / 1e3)} KiB)`);
console.log(`> ${total / n + "ms"}`);
console.log(`> ${total / n + 'ms'}`);
}
}
}
+54 -47
View File
@@ -9,25 +9,29 @@ export interface ArrowFunctionsBindingOptions {
*
* @default false
*/
spec?: boolean
spec?: boolean;
}
export interface Es2015BindingOptions {
/** Transform arrow functions into function expressions. */
arrowFunction?: ArrowFunctionsBindingOptions
arrowFunction?: ArrowFunctionsBindingOptions;
}
/** TypeScript Isolated Declarations for Standalone DTS Emit */
export declare function isolatedDeclaration(filename: string, sourceText: string, options: IsolatedDeclarationsOptions): IsolatedDeclarationsResult
export declare function isolatedDeclaration(
filename: string,
sourceText: string,
options: IsolatedDeclarationsOptions,
): IsolatedDeclarationsResult;
export interface IsolatedDeclarationsOptions {
sourcemap: boolean
sourcemap: boolean;
}
export interface IsolatedDeclarationsResult {
code: string
map?: SourceMap
errors: Array<string>
code: string;
map?: SourceMap;
errors: Array<string>;
}
/**
@@ -44,7 +48,7 @@ export interface ReactBindingOptions {
*
* @default 'automatic'
*/
runtime?: 'classic' | 'automatic'
runtime?: 'classic' | 'automatic';
/**
* Emit development-specific information, such as `__source` and `__self`.
*
@@ -52,7 +56,7 @@ export interface ReactBindingOptions {
*
* @see [@babel/plugin-transform-react-jsx-development](https://babeljs.io/docs/babel-plugin-transform-react-jsx-development)
*/
development?: boolean
development?: boolean;
/**
* Toggles whether or not to throw an error if an XML namespaced tag name
* is used.
@@ -62,7 +66,7 @@ export interface ReactBindingOptions {
*
* @default true
*/
throwIfNamespace?: boolean
throwIfNamespace?: boolean;
/**
* Enables [@babel/plugin-transform-react-pure-annotations](https://babeljs.io/docs/en/babel-plugin-transform-react-pure-annotations).
*
@@ -70,13 +74,13 @@ export interface ReactBindingOptions {
*
* @default true
*/
pure?: boolean
pure?: boolean;
/**
* Replaces the import source when importing functions.
*
* @default 'react'
*/
importSource?: string
importSource?: string;
/**
* Replace the function used when compiling JSX expressions. It should be a
* qualified name (e.g. `React.createElement`) or an identifier (e.g.
@@ -86,7 +90,7 @@ export interface ReactBindingOptions {
*
* @default 'React.createElement'
*/
pragma?: string
pragma?: string;
/**
* Replace the component used when compiling JSX fragments. It should be a
* valid JSX tag name.
@@ -95,7 +99,7 @@ export interface ReactBindingOptions {
*
* @default 'React.Fragment'
*/
pragmaFrag?: string
pragmaFrag?: string;
/**
* When spreading props, use `Object.assign` directly instead of an extend helper.
*
@@ -103,7 +107,7 @@ export interface ReactBindingOptions {
*
* @default false
*/
useBuiltIns?: boolean
useBuiltIns?: boolean;
/**
* When spreading props, use inline object with spread elements directly
* instead of an extend helper or Object.assign.
@@ -112,9 +116,9 @@ export interface ReactBindingOptions {
*
* @default false
*/
useSpread?: boolean
useSpread?: boolean;
/** Enable react fast refresh transform */
refresh?: ReactRefreshBindingOptions
refresh?: ReactRefreshBindingOptions;
}
export interface ReactRefreshBindingOptions {
@@ -123,23 +127,23 @@ export interface ReactRefreshBindingOptions {
*
* @default `$RefreshReg$`.
*/
refreshReg?: string
refreshReg?: string;
/**
* Specify the identifier of the refresh signature variable.
*
* @default `$RefreshSig$`.
*/
refreshSig?: string
emitFullSignatures?: boolean
refreshSig?: string;
emitFullSignatures?: boolean;
}
export interface SourceMap {
file?: string
mappings?: string
sourceRoot?: string
sources?: Array<string | undefined | null>
sourcesContent?: Array<string | undefined | null>
names?: Array<string>
file?: string;
mappings?: string;
sourceRoot?: string;
sources?: Array<string | undefined | null>;
sourcesContent?: Array<string | undefined | null>;
names?: Array<string>;
}
/**
@@ -154,7 +158,11 @@ export interface SourceMap {
* @returns an object containing the transformed code, source maps, and any
* errors that occurred during parsing or transformation.
*/
export declare function transform(filename: string, sourceText: string, options?: TransformOptions | undefined | null): TransformResult
export declare function transform(
filename: string,
sourceText: string,
options?: TransformOptions | undefined | null,
): TransformResult;
/**
* Options for transforming a JavaScript or TypeScript file.
@@ -162,24 +170,24 @@ export declare function transform(filename: string, sourceText: string, options?
* @see {@link transform}
*/
export interface TransformOptions {
sourceType?: 'script' | 'module' | 'unambiguous' | undefined
sourceType?: 'script' | 'module' | 'unambiguous' | undefined;
/**
* The current working directory. Used to resolve relative paths in other
* options.
*/
cwd?: string
cwd?: string;
/**
* Force jsx parsing,
*
* @default false
*/
jsx?: boolean
jsx?: boolean;
/** Configure how TypeScript is transformed. */
typescript?: TypeScriptBindingOptions
typescript?: TypeScriptBindingOptions;
/** Configure how TSX and JSX are transformed. */
react?: ReactBindingOptions
react?: ReactBindingOptions;
/** Enable ES2015 transformations. */
es2015?: Es2015BindingOptions
es2015?: Es2015BindingOptions;
/**
* Enable source map generation.
*
@@ -189,7 +197,7 @@ export interface TransformOptions {
*
* @see {@link SourceMap}
*/
sourcemap?: boolean
sourcemap?: boolean;
}
export interface TransformResult {
@@ -198,13 +206,13 @@ export interface TransformResult {
*
* If parsing failed, this will be an empty string.
*/
code: string
code: string;
/**
* The source map for the transformed code.
*
* This will be set if {@link TransformOptions#sourcemap} is `true`.
*/
map?: SourceMap
map?: SourceMap;
/**
* The `.d.ts` declaration file for the transformed code. Declarations are
* only generated if `declaration` is set to `true` and a TypeScript file
@@ -215,13 +223,13 @@ export interface TransformResult {
* @see {@link TypeScriptBindingOptions#declaration}
* @see [declaration tsconfig option](https://www.typescriptlang.org/tsconfig/#declaration)
*/
declaration?: string
declaration?: string;
/**
* Declaration source map. Only generated if both
* {@link TypeScriptBindingOptions#declaration declaration} and
* {@link TransformOptions#sourcemap sourcemap} are set to `true`.
*/
declarationMap?: SourceMap
declarationMap?: SourceMap;
/**
* Parse and transformation errors.
*
@@ -229,15 +237,15 @@ export interface TransformResult {
* transformed code may still be available even if there are errors in this
* list.
*/
errors: Array<string>
errors: Array<string>;
}
export interface TypeScriptBindingOptions {
jsxPragma?: string
jsxPragmaFrag?: string
onlyRemoveTypeImports?: boolean
allowNamespaces?: boolean
allowDeclareFields?: boolean
jsxPragma?: string;
jsxPragmaFrag?: string;
onlyRemoveTypeImports?: boolean;
allowNamespaces?: boolean;
allowDeclareFields?: boolean;
/**
* Also generate a `.d.ts` declaration file for TypeScript files.
*
@@ -247,7 +255,7 @@ export interface TypeScriptBindingOptions {
*
* @default false
*/
declaration?: boolean
declaration?: boolean;
/**
* Rewrite or remove TypeScript import/export declaration extensions.
*
@@ -258,6 +266,5 @@ export interface TypeScriptBindingOptions {
*
* @default false
*/
rewriteImportExtensions?: 'rewrite' | 'remove' | boolean
rewriteImportExtensions?: 'rewrite' | 'remove' | boolean;
}
+162 -181
View File
@@ -2,349 +2,330 @@
/* eslint-disable */
/* auto-generated by NAPI-RS */
const { readFileSync } = require('fs')
const { readFileSync } = require('fs');
let nativeBinding = null
const loadErrors = []
let nativeBinding = null;
const loadErrors = [];
const isMusl = () => {
let musl = false
let musl = false;
if (process.platform === 'linux') {
musl = isMuslFromFilesystem()
musl = isMuslFromFilesystem();
if (musl === null) {
musl = isMuslFromReport()
musl = isMuslFromReport();
}
if (musl === null) {
musl = isMuslFromChildProcess()
musl = isMuslFromChildProcess();
}
}
return musl
}
return musl;
};
const isFileMusl = (f) => f.includes('libc.musl-') || f.includes('ld-musl-')
const isFileMusl = (f) => f.includes('libc.musl-') || f.includes('ld-musl-');
const isMuslFromFilesystem = () => {
try {
return readFileSync('/usr/bin/ldd', 'utf-8').includes('musl')
return readFileSync('/usr/bin/ldd', 'utf-8').includes('musl');
} catch {
return null
return null;
}
}
};
const isMuslFromReport = () => {
const report = typeof process.report.getReport === 'function' ? process.report.getReport() : null
const report = typeof process.report.getReport === 'function' ? process.report.getReport() : null;
if (!report) {
return null
return null;
}
if (report.header && report.header.glibcVersionRuntime) {
return false
return false;
}
if (Array.isArray(report.sharedObjects)) {
if (report.sharedObjects.some(isFileMusl)) {
return true
return true;
}
}
return false
}
return false;
};
const isMuslFromChildProcess = () => {
try {
return require('child_process').execSync('ldd --version', { encoding: 'utf8' }).includes('musl')
return require('child_process').execSync('ldd --version', { encoding: 'utf8' }).includes('musl');
} catch (e) {
// If we reach this case, we don't know if the system is musl or not, so is better to just fallback to false
return false
return false;
}
}
};
function requireNative() {
if (process.platform === 'android') {
if (process.arch === 'arm64') {
try {
return require('./transform.android-arm64.node')
return require('./transform.android-arm64.node');
} catch (e) {
loadErrors.push(e)
loadErrors.push(e);
}
try {
return require('@oxc-transform/binding-android-arm64')
return require('@oxc-transform/binding-android-arm64');
} catch (e) {
loadErrors.push(e)
loadErrors.push(e);
}
} else if (process.arch === 'arm') {
try {
return require('./transform.android-arm-eabi.node')
return require('./transform.android-arm-eabi.node');
} catch (e) {
loadErrors.push(e)
loadErrors.push(e);
}
try {
return require('@oxc-transform/binding-android-arm-eabi')
return require('@oxc-transform/binding-android-arm-eabi');
} catch (e) {
loadErrors.push(e)
loadErrors.push(e);
}
} else {
loadErrors.push(new Error(`Unsupported architecture on Android ${process.arch}`))
loadErrors.push(new Error(`Unsupported architecture on Android ${process.arch}`));
}
} else if (process.platform === 'win32') {
if (process.arch === 'x64') {
try {
return require('./transform.win32-x64-msvc.node')
return require('./transform.win32-x64-msvc.node');
} catch (e) {
loadErrors.push(e)
loadErrors.push(e);
}
try {
return require('@oxc-transform/binding-win32-x64-msvc')
return require('@oxc-transform/binding-win32-x64-msvc');
} catch (e) {
loadErrors.push(e)
loadErrors.push(e);
}
} else if (process.arch === 'ia32') {
try {
return require('./transform.win32-ia32-msvc.node')
return require('./transform.win32-ia32-msvc.node');
} catch (e) {
loadErrors.push(e)
loadErrors.push(e);
}
try {
return require('@oxc-transform/binding-win32-ia32-msvc')
return require('@oxc-transform/binding-win32-ia32-msvc');
} catch (e) {
loadErrors.push(e)
loadErrors.push(e);
}
} else if (process.arch === 'arm64') {
try {
return require('./transform.win32-arm64-msvc.node')
return require('./transform.win32-arm64-msvc.node');
} catch (e) {
loadErrors.push(e)
loadErrors.push(e);
}
try {
return require('@oxc-transform/binding-win32-arm64-msvc')
return require('@oxc-transform/binding-win32-arm64-msvc');
} catch (e) {
loadErrors.push(e)
loadErrors.push(e);
}
} else {
loadErrors.push(new Error(`Unsupported architecture on Windows: ${process.arch}`))
loadErrors.push(new Error(`Unsupported architecture on Windows: ${process.arch}`));
}
} else if (process.platform === 'darwin') {
try {
return require('./transform.darwin-universal.node')
} catch (e) {
loadErrors.push(e)
}
try {
return require('@oxc-transform/binding-darwin-universal')
} catch (e) {
loadErrors.push(e)
}
return require('./transform.darwin-universal.node');
} catch (e) {
loadErrors.push(e);
}
try {
return require('@oxc-transform/binding-darwin-universal');
} catch (e) {
loadErrors.push(e);
}
if (process.arch === 'x64') {
try {
return require('./transform.darwin-x64.node')
return require('./transform.darwin-x64.node');
} catch (e) {
loadErrors.push(e)
loadErrors.push(e);
}
try {
return require('@oxc-transform/binding-darwin-x64')
return require('@oxc-transform/binding-darwin-x64');
} catch (e) {
loadErrors.push(e)
loadErrors.push(e);
}
} else if (process.arch === 'arm64') {
try {
return require('./transform.darwin-arm64.node')
return require('./transform.darwin-arm64.node');
} catch (e) {
loadErrors.push(e)
loadErrors.push(e);
}
try {
return require('@oxc-transform/binding-darwin-arm64')
return require('@oxc-transform/binding-darwin-arm64');
} catch (e) {
loadErrors.push(e)
loadErrors.push(e);
}
} else {
loadErrors.push(new Error(`Unsupported architecture on macOS: ${process.arch}`))
loadErrors.push(new Error(`Unsupported architecture on macOS: ${process.arch}`));
}
} else if (process.platform === 'freebsd') {
if (process.arch === 'x64') {
try {
return require('./transform.freebsd-x64.node')
return require('./transform.freebsd-x64.node');
} catch (e) {
loadErrors.push(e)
loadErrors.push(e);
}
try {
return require('@oxc-transform/binding-freebsd-x64')
return require('@oxc-transform/binding-freebsd-x64');
} catch (e) {
loadErrors.push(e)
loadErrors.push(e);
}
} else if (process.arch === 'arm64') {
try {
return require('./transform.freebsd-arm64.node')
return require('./transform.freebsd-arm64.node');
} catch (e) {
loadErrors.push(e)
loadErrors.push(e);
}
try {
return require('@oxc-transform/binding-freebsd-arm64')
return require('@oxc-transform/binding-freebsd-arm64');
} catch (e) {
loadErrors.push(e)
loadErrors.push(e);
}
} else {
loadErrors.push(new Error(`Unsupported architecture on FreeBSD: ${process.arch}`))
loadErrors.push(new Error(`Unsupported architecture on FreeBSD: ${process.arch}`));
}
} else if (process.platform === 'linux') {
if (process.arch === 'x64') {
if (isMusl()) {
try {
return require('./transform.linux-x64-musl.node')
} catch (e) {
loadErrors.push(e)
}
try {
return require('@oxc-transform/binding-linux-x64-musl')
} catch (e) {
loadErrors.push(e)
}
return require('./transform.linux-x64-musl.node');
} catch (e) {
loadErrors.push(e);
}
try {
return require('@oxc-transform/binding-linux-x64-musl');
} catch (e) {
loadErrors.push(e);
}
} else {
try {
return require('./transform.linux-x64-gnu.node')
} catch (e) {
loadErrors.push(e)
}
try {
return require('@oxc-transform/binding-linux-x64-gnu')
} catch (e) {
loadErrors.push(e)
}
return require('./transform.linux-x64-gnu.node');
} catch (e) {
loadErrors.push(e);
}
try {
return require('@oxc-transform/binding-linux-x64-gnu');
} catch (e) {
loadErrors.push(e);
}
}
} else if (process.arch === 'arm64') {
if (isMusl()) {
try {
return require('./transform.linux-arm64-musl.node')
} catch (e) {
loadErrors.push(e)
}
try {
return require('@oxc-transform/binding-linux-arm64-musl')
} catch (e) {
loadErrors.push(e)
}
return require('./transform.linux-arm64-musl.node');
} catch (e) {
loadErrors.push(e);
}
try {
return require('@oxc-transform/binding-linux-arm64-musl');
} catch (e) {
loadErrors.push(e);
}
} else {
try {
return require('./transform.linux-arm64-gnu.node')
} catch (e) {
loadErrors.push(e)
}
try {
return require('@oxc-transform/binding-linux-arm64-gnu')
} catch (e) {
loadErrors.push(e)
}
return require('./transform.linux-arm64-gnu.node');
} catch (e) {
loadErrors.push(e);
}
try {
return require('@oxc-transform/binding-linux-arm64-gnu');
} catch (e) {
loadErrors.push(e);
}
}
} else if (process.arch === 'arm') {
if (isMusl()) {
try {
return require('./transform.linux-arm-musleabihf.node')
} catch (e) {
loadErrors.push(e)
}
try {
return require('@oxc-transform/binding-linux-arm-musleabihf')
} catch (e) {
loadErrors.push(e)
}
return require('./transform.linux-arm-musleabihf.node');
} catch (e) {
loadErrors.push(e);
}
try {
return require('@oxc-transform/binding-linux-arm-musleabihf');
} catch (e) {
loadErrors.push(e);
}
} else {
try {
return require('./transform.linux-arm-gnueabihf.node')
} catch (e) {
loadErrors.push(e)
}
try {
return require('@oxc-transform/binding-linux-arm-gnueabihf')
} catch (e) {
loadErrors.push(e)
}
return require('./transform.linux-arm-gnueabihf.node');
} catch (e) {
loadErrors.push(e);
}
try {
return require('@oxc-transform/binding-linux-arm-gnueabihf');
} catch (e) {
loadErrors.push(e);
}
}
} else if (process.arch === 'riscv64') {
if (isMusl()) {
try {
return require('./transform.linux-riscv64-musl.node')
} catch (e) {
loadErrors.push(e)
}
try {
return require('@oxc-transform/binding-linux-riscv64-musl')
} catch (e) {
loadErrors.push(e)
}
return require('./transform.linux-riscv64-musl.node');
} catch (e) {
loadErrors.push(e);
}
try {
return require('@oxc-transform/binding-linux-riscv64-musl');
} catch (e) {
loadErrors.push(e);
}
} else {
try {
return require('./transform.linux-riscv64-gnu.node')
} catch (e) {
loadErrors.push(e)
}
try {
return require('@oxc-transform/binding-linux-riscv64-gnu')
} catch (e) {
loadErrors.push(e)
}
return require('./transform.linux-riscv64-gnu.node');
} catch (e) {
loadErrors.push(e);
}
try {
return require('@oxc-transform/binding-linux-riscv64-gnu');
} catch (e) {
loadErrors.push(e);
}
}
} else if (process.arch === 'ppc64') {
try {
return require('./transform.linux-ppc64-gnu.node')
return require('./transform.linux-ppc64-gnu.node');
} catch (e) {
loadErrors.push(e)
loadErrors.push(e);
}
try {
return require('@oxc-transform/binding-linux-ppc64-gnu')
return require('@oxc-transform/binding-linux-ppc64-gnu');
} catch (e) {
loadErrors.push(e)
loadErrors.push(e);
}
} else if (process.arch === 's390x') {
try {
return require('./transform.linux-s390x-gnu.node')
return require('./transform.linux-s390x-gnu.node');
} catch (e) {
loadErrors.push(e)
loadErrors.push(e);
}
try {
return require('@oxc-transform/binding-linux-s390x-gnu')
return require('@oxc-transform/binding-linux-s390x-gnu');
} catch (e) {
loadErrors.push(e)
loadErrors.push(e);
}
} else {
loadErrors.push(new Error(`Unsupported architecture on Linux: ${process.arch}`))
loadErrors.push(new Error(`Unsupported architecture on Linux: ${process.arch}`));
}
} else {
loadErrors.push(new Error(`Unsupported OS: ${process.platform}, architecture: ${process.arch}`))
loadErrors.push(new Error(`Unsupported OS: ${process.platform}, architecture: ${process.arch}`));
}
}
nativeBinding = requireNative()
nativeBinding = requireNative();
if (!nativeBinding || process.env.NAPI_RS_FORCE_WASI) {
try {
nativeBinding = require('./transform.wasi.cjs')
nativeBinding = require('./transform.wasi.cjs');
} catch (err) {
if (process.env.NAPI_RS_FORCE_WASI) {
console.error(err)
console.error(err);
}
}
if (!nativeBinding) {
try {
nativeBinding = require('@oxc-transform/binding-wasm32-wasi')
nativeBinding = require('@oxc-transform/binding-wasm32-wasi');
} catch (err) {
if (process.env.NAPI_RS_FORCE_WASI) {
console.error(err)
console.error(err);
}
}
}
@@ -356,10 +337,10 @@ if (!nativeBinding) {
// - The package owner could build/publish bindings for this arch
// - The user may need to bundle the correct files
// - The user may need to re-install node_modules to get new packages
throw new Error('Failed to load native binding', { cause: loadErrors })
throw new Error('Failed to load native binding', { cause: loadErrors });
}
throw new Error(`Failed to load native binding`)
throw new Error(`Failed to load native binding`);
}
module.exports.isolatedDeclaration = nativeBinding.isolatedDeclaration
module.exports.transform = nativeBinding.transform
module.exports.isolatedDeclaration = nativeBinding.isolatedDeclaration;
module.exports.transform = nativeBinding.transform;
+7 -7
View File
@@ -1,15 +1,15 @@
import oxc from "./index.js";
import assert from "assert";
import assert from 'assert';
import oxc from './index.js';
console.log(`Testing on ${process.platform}-${process.arch}`);
test(oxc.isolatedDeclaration("test.ts", "class A {}", { sourcemap: true }), {
code: "declare class A {}\n",
test(oxc.isolatedDeclaration('test.ts', 'class A {}', { sourcemap: true }), {
code: 'declare class A {}\n',
map: {
mappings: "AAAA,cAAM,EAAE,CAAE",
mappings: 'AAAA,cAAM,EAAE,CAAE',
names: [],
sources: ["test.ts"],
sourcesContent: ["class A {}"],
sources: ['test.ts'],
sourcesContent: ['class A {}'],
},
});
+3 -3
View File
@@ -5,8 +5,8 @@ See index.d.ts for `parseSync` and `parseAsync` API.
## ESM
```javascript
import oxc from "oxc-parser";
import assert from "assert";
import assert from 'assert';
import oxc from 'oxc-parser';
function test(ret) {
const program = JSON.parse(ret.program);
@@ -16,7 +16,7 @@ function test(ret) {
const sourceText = "let foo: Foo = 'foo';";
const options = {
sourceFilename: "text.tsx", // the extension is used to determine which dialect to parse
sourceFilename: 'text.tsx', // the extension is used to determine which dialect to parse
};
test(oxc.parseSync(sourceText, options));
+29 -29
View File
@@ -1,13 +1,13 @@
// Code copied from [Rome](https://github.com/rome/tools/blob/main/npm/rome/scripts/generate-packages.mjs)
import { resolve } from "node:path";
import { fileURLToPath } from "node:url";
import * as fs from "node:fs";
import * as fs from 'node:fs';
import { resolve } from 'node:path';
import { fileURLToPath } from 'node:url';
const OXC_ROOT = resolve(fileURLToPath(import.meta.url), "../..");
const PACKAGES_ROOT = resolve(OXC_ROOT, "..");
const BINARY_ROOT = resolve(OXC_ROOT, "../../napi/parser");
const MANIFEST_PATH = resolve(OXC_ROOT, "package.json");
const OXC_ROOT = resolve(fileURLToPath(import.meta.url), '../..');
const PACKAGES_ROOT = resolve(OXC_ROOT, '..');
const BINARY_ROOT = resolve(OXC_ROOT, '../../napi/parser');
const MANIFEST_PATH = resolve(OXC_ROOT, 'package.json');
console.log('OXC_ROOT', OXC_ROOT);
console.log('PACKAGES_ROOT', PACKAGES_ROOT);
@@ -15,16 +15,16 @@ console.log('BINARY_ROOT', BINARY_ROOT);
console.log('MANIFEST_PATH', MANIFEST_PATH);
const LIBC_MAPPING = {
"gnu": "glibc",
"musl": "musl",
}
'gnu': 'glibc',
'musl': 'musl',
};
const rootManifest = JSON.parse(
fs.readFileSync(MANIFEST_PATH).toString("utf-8")
fs.readFileSync(MANIFEST_PATH).toString('utf-8'),
);
function package_name(target) {
return `@oxc-parser/binding-${target}`
return `@oxc-parser/binding-${target}`;
}
function generateNativePackage(target) {
const binaryName = `parser.${target}.node`;
@@ -44,10 +44,10 @@ function generateNativePackage(target) {
// Generate the package.json manifest
const { version, author, license, homepage, bugs, repository } = rootManifest;
const triple = target.split("-");
const triple = target.split('-');
const platform = triple[0];
const arch = triple[1];
const libc = triple[2] && LIBC_MAPPING[triple[2]] && { libc: [LIBC_MAPPING[triple[2]]] }
const libc = triple[2] && LIBC_MAPPING[triple[2]] && { libc: [LIBC_MAPPING[triple[2]]] };
const manifest = {
name: package_name(target),
version,
@@ -60,10 +60,10 @@ function generateNativePackage(target) {
files: [binaryName],
cpu: [arch],
os: [platform],
...libc
...libc,
};
const manifestPath = resolve(packageRoot, "package.json");
const manifestPath = resolve(packageRoot, 'package.json');
console.log(`Create manifest ${manifestPath}`);
fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2));
@@ -73,12 +73,12 @@ function generateNativePackage(target) {
function writeManifest() {
const packageRoot = resolve(PACKAGES_ROOT, 'oxc-parser');
const manifestPath = resolve(packageRoot, "package.json");
const manifestPath = resolve(packageRoot, 'package.json');
console.log('packageRoot', packageRoot);
const manifestData = JSON.parse(
fs.readFileSync(manifestPath).toString("utf-8")
fs.readFileSync(manifestPath).toString('utf-8'),
);
const nativePackages = TARGETS.map((target) => [
@@ -86,8 +86,8 @@ function writeManifest() {
rootManifest.version,
]);
manifestData["version"] = rootManifest.version;
manifestData["optionalDependencies"] = Object.fromEntries(nativePackages);
manifestData['version'] = rootManifest.version;
manifestData['optionalDependencies'] = Object.fromEntries(nativePackages);
console.log('manifestPath', manifestPath);
console.log('manifestData', manifestData);
@@ -95,7 +95,7 @@ function writeManifest() {
const content = JSON.stringify(manifestData, null, 2);
fs.writeFileSync(manifestPath, content);
let files = ["index.js", "index.d.ts"];
let files = ['index.js', 'index.d.ts'];
for (const file of files) {
fs.copyFileSync(resolve(BINARY_ROOT, file), resolve(packageRoot, file));
}
@@ -103,14 +103,14 @@ function writeManifest() {
// NOTE: Must update npm/oxc-parser/package.json
const TARGETS = [
"win32-x64-msvc",
"win32-arm64-msvc",
"linux-x64-gnu",
"linux-arm64-gnu",
"linux-x64-musl",
"linux-arm64-musl",
"darwin-x64",
"darwin-arm64",
'win32-x64-msvc',
'win32-arm64-msvc',
'linux-x64-gnu',
'linux-arm64-gnu',
'linux-x64-musl',
'linux-arm64-musl',
'darwin-x64',
'darwin-arm64',
];
for (const target of TARGETS) {
+13 -9
View File
@@ -12,14 +12,14 @@ This is still in alpha and may yield incorrect results, feel free to [submit a b
import assert from 'assert';
import oxc from 'oxc-transform';
const { sourceMap, sourceText, errors } = oxc.isolatedDeclaration("test.ts", "class A {}", { sourcemap: true });
const { sourceMap, sourceText, errors } = oxc.isolatedDeclaration('test.ts', 'class A {}', { sourcemap: true });
assert.equal(sourceText, "declare class A {}\n");
assert.equal(sourceText, 'declare class A {}\n');
assert.deepEqual(ret.sourceMap, {
mappings: "AAAA,cAAM,EAAE,CAAE",
mappings: 'AAAA,cAAM,EAAE,CAAE',
names: [],
sources: ["test.ts"],
sourcesContent: ["class A {}"],
sources: ['test.ts'],
sourcesContent: ['class A {}'],
});
assert(errors.length == 0);
```
@@ -27,14 +27,18 @@ assert(errors.length == 0);
### API
```typescript
export function isolatedDeclaration(filename: string, sourceText: string, options: IsolatedDeclarationsOptions): IsolatedDeclarationsResult
export function isolatedDeclaration(
filename: string,
sourceText: string,
options: IsolatedDeclarationsOptions,
): IsolatedDeclarationsResult;
export interface IsolatedDeclarationsOptions {
sourcemap: boolean
sourcemap: boolean;
}
export interface IsolatedDeclarationsResult {
sourceText: string
errors: Array<string>
sourceText: string;
errors: Array<string>;
}
```
+29 -29
View File
@@ -1,13 +1,13 @@
// Code copied from [Rome](https://github.com/rome/tools/blob/main/npm/rome/scripts/generate-packages.mjs)
import { resolve } from "node:path";
import { fileURLToPath } from "node:url";
import * as fs from "node:fs";
import * as fs from 'node:fs';
import { resolve } from 'node:path';
import { fileURLToPath } from 'node:url';
const OXC_ROOT = resolve(fileURLToPath(import.meta.url), "../..");
const PACKAGES_ROOT = resolve(OXC_ROOT, "..");
const BINARY_ROOT = resolve(OXC_ROOT, "../../napi/transform");
const MANIFEST_PATH = resolve(OXC_ROOT, "package.json");
const OXC_ROOT = resolve(fileURLToPath(import.meta.url), '../..');
const PACKAGES_ROOT = resolve(OXC_ROOT, '..');
const BINARY_ROOT = resolve(OXC_ROOT, '../../napi/transform');
const MANIFEST_PATH = resolve(OXC_ROOT, 'package.json');
console.log('OXC_ROOT', OXC_ROOT);
console.log('PACKAGES_ROOT', PACKAGES_ROOT);
@@ -15,16 +15,16 @@ console.log('BINARY_ROOT', BINARY_ROOT);
console.log('MANIFEST_PATH', MANIFEST_PATH);
const LIBC_MAPPING = {
"gnu": "glibc",
"musl": "musl",
}
'gnu': 'glibc',
'musl': 'musl',
};
const rootManifest = JSON.parse(
fs.readFileSync(MANIFEST_PATH).toString("utf-8")
fs.readFileSync(MANIFEST_PATH).toString('utf-8'),
);
function package_name(target) {
return `@oxc-transform/binding-${target}`
return `@oxc-transform/binding-${target}`;
}
function generateNativePackage(target) {
const binaryName = `transform.${target}.node`;
@@ -44,10 +44,10 @@ function generateNativePackage(target) {
// Generate the package.json manifest
const { version, author, license, homepage, bugs, repository } = rootManifest;
const triple = target.split("-");
const triple = target.split('-');
const platform = triple[0];
const arch = triple[1];
const libc = triple[2] && LIBC_MAPPING[triple[2]] && { libc: [LIBC_MAPPING[triple[2]]] }
const libc = triple[2] && LIBC_MAPPING[triple[2]] && { libc: [LIBC_MAPPING[triple[2]]] };
const manifest = {
name: package_name(target),
version,
@@ -60,10 +60,10 @@ function generateNativePackage(target) {
files: [binaryName],
cpu: [arch],
os: [platform],
...libc
...libc,
};
const manifestPath = resolve(packageRoot, "package.json");
const manifestPath = resolve(packageRoot, 'package.json');
console.log(`Create manifest ${manifestPath}`);
fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2));
@@ -73,12 +73,12 @@ function generateNativePackage(target) {
function writeManifest() {
const packageRoot = resolve(PACKAGES_ROOT, 'oxc-transform');
const manifestPath = resolve(packageRoot, "package.json");
const manifestPath = resolve(packageRoot, 'package.json');
console.log('packageRoot', packageRoot);
const manifestData = JSON.parse(
fs.readFileSync(manifestPath).toString("utf-8")
fs.readFileSync(manifestPath).toString('utf-8'),
);
const nativePackages = TARGETS.map((target) => [
@@ -86,8 +86,8 @@ function writeManifest() {
rootManifest.version,
]);
manifestData["version"] = rootManifest.version;
manifestData["optionalDependencies"] = Object.fromEntries(nativePackages);
manifestData['version'] = rootManifest.version;
manifestData['optionalDependencies'] = Object.fromEntries(nativePackages);
console.log('manifestPath', manifestPath);
console.log('manifestData', manifestData);
@@ -95,7 +95,7 @@ function writeManifest() {
const content = JSON.stringify(manifestData, null, 2);
fs.writeFileSync(manifestPath, content);
let files = ["index.js", "index.d.ts"];
let files = ['index.js', 'index.d.ts'];
for (const file of files) {
fs.copyFileSync(resolve(BINARY_ROOT, file), resolve(packageRoot, file));
}
@@ -103,14 +103,14 @@ function writeManifest() {
// NOTE: Must update npm/oxc-transform/package.json
const TARGETS = [
"win32-x64-msvc",
"win32-arm64-msvc",
"linux-x64-gnu",
"linux-arm64-gnu",
"linux-x64-musl",
"linux-arm64-musl",
"darwin-x64",
"darwin-arm64",
'win32-x64-msvc',
'win32-arm64-msvc',
'linux-x64-gnu',
'linux-arm64-gnu',
'linux-x64-musl',
'linux-arm64-musl',
'darwin-x64',
'darwin-arm64',
];
for (const target of TARGETS) {
+3 -3
View File
@@ -43,6 +43,6 @@ See [usage instructions](https://oxc.rs/docs/guide/usage/linter.html).
Run
* `npx --yes oxlint@latest` in your JavaScript / TypeScript codebase and see it complete in milliseconds. No configurations are required.
* `npx oxlint@latest --help` for quick usage instructions.
* `npx oxlint@latest --rules` for the list of rules.
- `npx --yes oxlint@latest` in your JavaScript / TypeScript codebase and see it complete in milliseconds. No configurations are required.
- `npx oxlint@latest --help` for quick usage instructions.
- `npx oxlint@latest --rules` for the list of rules.
+1 -1
View File
@@ -295,4 +295,4 @@
]
}
}
}
}
+1 -1
View File
@@ -25,4 +25,4 @@
"configuration_schema.json",
"README.md"
]
}
}
+32 -32
View File
@@ -1,24 +1,24 @@
// Code copied from [Rome](https://github.com/rome/tools/blob/main/npm/rome/scripts/generate-packages.mjs)
import { resolve } from "node:path";
import { fileURLToPath } from "node:url";
import * as fs from "node:fs";
import * as fs from 'node:fs';
import { resolve } from 'node:path';
import { fileURLToPath } from 'node:url';
const OXLINT_BIN_NAME = "oxlint";
const OXLS_BIN_NAME = "oxc_language_server";
const OXLINT_ROOT = resolve(fileURLToPath(import.meta.url), "../..");
const PACKAGES_ROOT = resolve(OXLINT_ROOT, "..");
const REPO_ROOT = resolve(PACKAGES_ROOT, "..");
const MANIFEST_PATH = resolve(OXLINT_ROOT, "package.json");
const OXLINT_BIN_NAME = 'oxlint';
const OXLS_BIN_NAME = 'oxc_language_server';
const OXLINT_ROOT = resolve(fileURLToPath(import.meta.url), '../..');
const PACKAGES_ROOT = resolve(OXLINT_ROOT, '..');
const REPO_ROOT = resolve(PACKAGES_ROOT, '..');
const MANIFEST_PATH = resolve(OXLINT_ROOT, 'package.json');
const rootManifest = JSON.parse(
fs.readFileSync(MANIFEST_PATH).toString("utf-8")
fs.readFileSync(MANIFEST_PATH).toString('utf-8'),
);
const LIBC_MAPPING = {
"gnu": "glibc",
"musl": "musl",
}
'gnu': 'glibc',
'musl': 'musl',
};
function generateNativePackage(target) {
const packageName = `@${OXLINT_BIN_NAME}/${target}`;
@@ -35,10 +35,10 @@ function generateNativePackage(target) {
// Generate the package.json manifest
const { version, author, license, homepage, bugs, repository } = rootManifest;
const triple = target.split("-");
const triple = target.split('-');
const platform = triple[0];
const arch = triple[1];
const libc = triple[2] && { libc: [LIBC_MAPPING[triple[2]]] }
const libc = triple[2] && { libc: [LIBC_MAPPING[triple[2]]] };
const manifest = {
name: packageName,
version,
@@ -49,15 +49,15 @@ function generateNativePackage(target) {
repository,
os: [platform],
cpu: [arch],
...libc
...libc,
};
const manifestPath = resolve(packageRoot, "package.json");
const manifestPath = resolve(packageRoot, 'package.json');
console.log(`Create manifest ${manifestPath}`);
fs.writeFileSync(manifestPath, JSON.stringify(manifest));
// Copy the binary
const ext = platform === "win32" ? ".exe" : "";
const ext = platform === 'win32' ? '.exe' : '';
const oxlintBinSource = resolve(REPO_ROOT, `${OXLINT_BIN_NAME}-${target}${ext}`);
const oxlintBinTarget = resolve(packageRoot, `${OXLINT_BIN_NAME}${ext}`);
@@ -75,10 +75,10 @@ function generateNativePackage(target) {
}
function writeManifest() {
const manifestPath = resolve(PACKAGES_ROOT, OXLINT_BIN_NAME, "package.json");
const manifestPath = resolve(PACKAGES_ROOT, OXLINT_BIN_NAME, 'package.json');
const manifestData = JSON.parse(
fs.readFileSync(manifestPath).toString("utf-8")
fs.readFileSync(manifestPath).toString('utf-8'),
);
const nativePackages = TARGETS.map((target) => [
@@ -86,8 +86,8 @@ function writeManifest() {
rootManifest.version,
]);
manifestData["version"] = rootManifest.version;
manifestData["optionalDependencies"] = Object.fromEntries(nativePackages);
manifestData['version'] = rootManifest.version;
manifestData['optionalDependencies'] = Object.fromEntries(nativePackages);
console.log(`Update manifest ${manifestPath}`);
const content = JSON.stringify(manifestData);
@@ -97,18 +97,18 @@ function writeManifest() {
// NOTE: Must update npm/oxlint/bin/oxlint
// and npm/oxlint/bin/oxc_language_server
const TARGETS = [
"win32-x64",
"win32-arm64",
"linux-x64-gnu",
"linux-arm64-gnu",
"linux-x64-musl",
"linux-arm64-musl",
"darwin-x64",
"darwin-arm64",
]
'win32-x64',
'win32-arm64',
'linux-x64-gnu',
'linux-arm64-gnu',
'linux-x64-musl',
'linux-arm64-musl',
'darwin-x64',
'darwin-arm64',
];
for (const target of TARGETS) {
generateNativePackage(target);
generateNativePackage(target);
}
writeManifest();
+42 -34
View File
@@ -9,19 +9,19 @@
* then shuts itself down.
*/
import {createWriteStream} from 'fs';
import fs from 'fs/promises';
import {join as pathJoin} from 'path';
import {pipeline} from 'stream/promises';
import express from 'express';
import {extract} from 'tar';
import { createWriteStream } from 'fs';
import fs from 'fs/promises';
import { join as pathJoin } from 'path';
import { pipeline } from 'stream/promises';
import { extract } from 'tar';
const DEFAULT_PORT = 3000,
LISTEN_ATTEMPTS = 10;
LISTEN_ATTEMPTS = 10;
// Create directory for saving assets
const rand = Math.round(Math.random() * 100000000000000).toString(16),
dataDir = `/tmp/oxc_bench_data_${rand}`;
dataDir = `/tmp/oxc_bench_data_${rand}`;
await fs.mkdir(dataDir);
let component = process.env.COMPONENT;
@@ -29,61 +29,69 @@ if (process.env.FIXTURE) component += process.env.FIXTURE;
const app = express();
const wrapHandler = fn => (req, res, next) => { fn(req, res).catch(next); };
const wrapHandler = fn => (req, res, next) => {
fn(req, res).catch(next);
};
const getFilePath = filename => pathJoin(dataDir, `${component}_${filename}`);
app.post('/upload', wrapHandler(async (req, res) => {
app.post(
'/upload',
wrapHandler(async (req, res) => {
const stream = createWriteStream(getFilePath('metadata.json'));
await pipeline(req, stream);
res.json({
status: 'success',
uploadUrl: `http://localhost:${port}/upload_archive`,
runId: 'dummy_value',
status: 'success',
uploadUrl: `http://localhost:${port}/upload_archive`,
runId: 'dummy_value',
});
}));
}),
);
app.put('/upload_archive', wrapHandler(async (req, res) => {
app.put(
'/upload_archive',
wrapHandler(async (req, res) => {
// Stream uploaded tarball to file
const path = getFilePath('archive.tar.gz');
const stream = createWriteStream(path);
await pipeline(req, stream);
// Untar contents + delete tarball
await extract({file: path, cwd: dataDir});
await extract({ file: path, cwd: dataDir });
await fs.rm(path);
// Rename `.out` files + delete `.log` files
const filenames = await fs.readdir(dataDir);
for (const filename of filenames) {
if (filename.endsWith('.log')) {
await fs.rm(pathJoin(dataDir, filename));
} else if (filename.endsWith('.out')) {
await fs.rename(pathJoin(dataDir, filename), getFilePath(filename));
}
if (filename.endsWith('.log')) {
await fs.rm(pathJoin(dataDir, filename));
} else if (filename.endsWith('.out')) {
await fs.rename(pathJoin(dataDir, filename), getFilePath(filename));
}
}
// Send response
res.send('');
server.close(() => {});
}));
}),
);
// Open server on a port which is not already in use
let server,
port = DEFAULT_PORT;
port = DEFAULT_PORT;
for (let i = 0; i < LISTEN_ATTEMPTS; i++) {
console.log(`Starting server on port ${port}`);
try {
await new Promise((resolve, reject) => {
server = app.listen(port, resolve);
server.on('error', reject);
});
break;
} catch (err) {
if (err?.code !== 'EADDRINUSE') throw err;
console.log(`Port ${port} in use. Trying again.`);
port = DEFAULT_PORT + Math.round(Math.random() * 5000);
}
console.log(`Starting server on port ${port}`);
try {
await new Promise((resolve, reject) => {
server = app.listen(port, resolve);
server.on('error', reject);
});
break;
} catch (err) {
if (err?.code !== 'EADDRINUSE') throw err;
console.log(`Port ${port} in use. Trying again.`);
port = DEFAULT_PORT + Math.round(Math.random() * 5000);
}
}
console.log(`Server listening on port ${port}`);
+1 -1
View File
@@ -6,7 +6,7 @@ import fs from 'fs';
// Create directory for saving assets
const rand = Math.round(Math.random() * 1000000000000000000).toString(16),
dataDir = `/tmp/oxc_bench_data_${rand}`;
dataDir = `/tmp/oxc_bench_data_${rand}`;
fs.mkdirSync(dataDir);
// Output dir path to env var
+59 -59
View File
@@ -2,34 +2,34 @@
* Combine benchmark data from different jobs, and upload to Codspeed.
*/
import {createReadStream} from 'fs';
import fs from 'fs/promises';
import {join as pathJoin} from 'path';
import {createHash} from 'crypto';
import assert from 'assert';
import {create as createTar} from 'tar';
import axios from 'axios';
import { createHash } from 'crypto';
import { createReadStream } from 'fs';
import fs from 'fs/promises';
import { join as pathJoin } from 'path';
import { create as createTar } from 'tar';
const METADATA_SUFFIX = '_metadata.json',
CODSPEED_UPLOAD_URL = 'https://api.codspeed.io/upload';
CODSPEED_UPLOAD_URL = 'https://api.codspeed.io/upload';
const dataDir = process.env.DATA_DIR,
token = process.env.CODSPEED_TOKEN;
token = process.env.CODSPEED_TOKEN;
// Find profile files and first metadata file
const profileFiles = [];
let metadataPath;
for (const filename of await fs.readdir(dataDir)) {
const path = pathJoin(dataDir, filename);
if (filename.endsWith(METADATA_SUFFIX)) {
if (!metadataPath) metadataPath = path;
} else {
const match = filename.match(/_(\d+)\.out$/);
assert(match, `Unexpected file: ${filename}`);
const path = pathJoin(dataDir, filename);
if (filename.endsWith(METADATA_SUFFIX)) {
if (!metadataPath) metadataPath = path;
} else {
const match = filename.match(/_(\d+)\.out$/);
assert(match, `Unexpected file: ${filename}`);
const pid = +match[1];
profileFiles.push({pid, path});
}
const pid = +match[1];
profileFiles.push({ pid, path });
}
}
// Move all `.out` files to one directory
@@ -39,49 +39,49 @@ const outDir = pathJoin(dataDir, 'out');
await fs.mkdir(outDir);
const pids = new Set(),
duplicates = [];
duplicates = [];
let highestPid = -1;
for (const {pid, path} of profileFiles) {
if (pids.has(pid)) {
// Duplicate PID
duplicates.push({pid, path});
} else {
pids.add(pid);
if (pid > highestPid) highestPid = pid;
await fs.rename(path, pathJoin(outDir, `${pid}.out`));
}
for (const { pid, path } of profileFiles) {
if (pids.has(pid)) {
// Duplicate PID
duplicates.push({ pid, path });
} else {
pids.add(pid);
if (pid > highestPid) highestPid = pid;
await fs.rename(path, pathJoin(outDir, `${pid}.out`));
}
}
// Alter PIDs for `.out` files with duplicate filenames
for (let {pid, path} of duplicates) {
let content = await fs.readFile(path, 'utf8');
for (let { pid, path } of duplicates) {
let content = await fs.readFile(path, 'utf8');
const pidLine = `\npid: ${pid}\n`;
const index = content.indexOf(pidLine);
assert(index !== -1, `Could not locate PID in ${path}`);
const before = content.slice(0, index);
assert(before.split('\n').length === 3, `Unexpected formatting in ${path}`);
const pidLine = `\npid: ${pid}\n`;
const index = content.indexOf(pidLine);
assert(index !== -1, `Could not locate PID in ${path}`);
const before = content.slice(0, index);
assert(before.split('\n').length === 3, `Unexpected formatting in ${path}`);
pid = ++highestPid;
content = `${before}\npid: ${pid}\n${content.slice(index + pidLine.length)}`;
pid = ++highestPid;
content = `${before}\npid: ${pid}\n${content.slice(index + pidLine.length)}`;
await fs.writeFile(pathJoin(outDir, `${pid}.out`), content);
await fs.rm(path);
await fs.writeFile(pathJoin(outDir, `${pid}.out`), content);
await fs.rm(path);
}
// ZIP combined profile directory
console.log('Zipping combined profile directory');
const archivePath = pathJoin(dataDir, 'archive.tar.gz');
await createTar({file: archivePath, gzip: true, cwd: outDir}, ['./']);
await createTar({ file: archivePath, gzip: true, cwd: outDir }, ['./']);
// Get size + MD5 hash of archive
console.log('Hashing ZIP');
const {size} = await fs.stat(archivePath);
const { size } = await fs.stat(archivePath);
const hash = createHash('md5');
const inputStream = createReadStream(archivePath);
for await (const chunk of inputStream) {
hash.update(chunk);
hash.update(chunk);
}
const md5 = hash.digest('base64');
@@ -97,33 +97,33 @@ metadata.profileMd5 = md5;
if (!token) metadata.tokenless = true;
const metadataJson = JSON.stringify(metadata);
if (!token) {
const metadataHash = createHash('sha256').update(metadataJson).digest('hex');
console.log(`CodSpeed Run Hash: "${metadataHash}"`);
const metadataHash = createHash('sha256').update(metadataJson).digest('hex');
console.log(`CodSpeed Run Hash: "${metadataHash}"`);
}
// Upload metadata to CodSpeed
console.log('Uploading metadata to CodSpeed');
const {data} = await axios({
method: 'post',
url: CODSPEED_UPLOAD_URL,
data: metadataJson,
headers: {
'Content-Type': 'application/json',
...(token ? {Authorization: token} : null),
},
const { data } = await axios({
method: 'post',
url: CODSPEED_UPLOAD_URL,
data: metadataJson,
headers: {
'Content-Type': 'application/json',
...(token ? { Authorization: token } : null),
},
});
assert(data?.status === 'success', 'Failed to upload metadata to Codspeed');
const {uploadUrl} = data;
const { uploadUrl } = data;
// Upload profile ZIP to Codspeed
console.log('Uploading profile ZIP to CodSpeed');
await axios({
method: 'put',
url: uploadUrl,
data: createReadStream(archivePath),
headers: {
'Content-Type': 'application/gzip',
'Content-Length': size,
'Content-MD5': md5,
}
method: 'put',
url: uploadUrl,
data: createReadStream(archivePath),
headers: {
'Content-Type': 'application/gzip',
'Content-Length': size,
'Content-MD5': md5,
},
});
+1
View File
@@ -31,6 +31,7 @@ UPDATE_SNAPSHOT=1 just c
```
<!-- Links -->
[test262]: https://github.com/tc39/test262
[babel]: https://github.com/babel/babel
[TypeScript]: https://github.com/microsoft/TypeScript
+1 -1
View File
@@ -1,3 +1,3 @@
{
"type": "module"
"type": "module"
}
+62 -63
View File
@@ -1,115 +1,114 @@
import vm from "node:vm"
import path from "node:path"
import fs from "node:fs"
import process from "node:process";
import { createServer } from "node:http";
import fs from 'node:fs';
import { createServer } from 'node:http';
import path from 'node:path';
import process from 'node:process';
import vm from 'node:vm';
const __dirname = path.dirname(new URL(import.meta.url).pathname);
const harnessDir = path.join(__dirname, '../..', 'test262', 'harness')
const harnessDir = path.join(__dirname, '../..', 'test262', 'harness');
const { Script, createContext, SourceTextModule, runInContext, SyntheticModule } = vm
const { Script, createContext, SourceTextModule, runInContext, SyntheticModule } = vm;
async function runCodeInHarness(options = {}) {
const { code = "", includes = [], importDir = "", isAsync = false, isModule = true, isRaw = false } = options
const context = {}
const { code = '', includes = [], importDir = '', isAsync = false, isModule = true, isRaw = false } = options;
const context = {};
// See: https://github.com/nodejs/node/issues/36351
const unique = () => '//' + Math.random()
const unique = () => '//' + Math.random();
const runCode = async () => {
const moduleCache = new Map
const dynamicImportCache = new Map
const moduleCache = new Map();
const dynamicImportCache = new Map();
const findModule = (modulePath) => {
let module = moduleCache.get(modulePath)
let module = moduleCache.get(modulePath);
if (!module) {
const code = fs.readFileSync(modulePath, 'utf8')
const code = fs.readFileSync(modulePath, 'utf8');
if (modulePath.endsWith('json')) {
const evaluate = function () {
this.setExport('default', runInContext('JSON.parse', context)(code))
}
module = new SyntheticModule(['default'], evaluate, { context })
const evaluate = function() {
this.setExport('default', runInContext('JSON.parse', context)(code));
};
module = new SyntheticModule(['default'], evaluate, { context });
} else {
module = new SourceTextModule(code + unique(), { context, importModuleDynamically })
module = new SourceTextModule(code + unique(), { context, importModuleDynamically });
}
moduleCache.set(modulePath, module)
moduleCache.set(modulePath, module);
}
return module
}
return module;
};
const linker = (specifier, referencingModule) => {
return findModule(path.join(importDir, specifier))
}
return findModule(path.join(importDir, specifier));
};
const importModuleDynamically = (specifier, script) => {
const where = path.join(importDir, specifier)
let promise = dynamicImportCache.get(where)
const where = path.join(importDir, specifier);
let promise = dynamicImportCache.get(where);
if (!promise) {
const module = findModule(where)
const module = findModule(where);
if (module.status === 'unlinked') {
promise = module.link(linker)
.then(() => module.evaluate())
.then(() => module)
.then(() => module);
} else {
promise = Promise.resolve(module)
promise = Promise.resolve(module);
}
dynamicImportCache.set(where, promise)
dynamicImportCache.set(where, promise);
}
return promise
}
return promise;
};
createContext(context)
if (!isRaw) runInContext(createHarnessForTest(includes), context)
createContext(context);
if (!isRaw) runInContext(createHarnessForTest(includes), context);
if (isModule) {
const module = new SourceTextModule(code + unique(), { context, importModuleDynamically })
await module.link(linker)
await module.evaluate()
const module = new SourceTextModule(code + unique(), { context, importModuleDynamically });
await module.link(linker);
await module.evaluate();
} else {
const script = new Script(code, { importModuleDynamically })
script.runInContext(context)
const script = new Script(code, { importModuleDynamically });
script.runInContext(context);
}
}
};
if (isAsync) {
await new Promise((resolve, reject) => {
context.$DONE = err => err ? reject(err) : resolve()
runCode().catch(reject)
})
context.$DONE = err => err ? reject(err) : resolve();
runCode().catch(reject);
});
} else {
await runCode()
await runCode();
}
}
const harnessFiles = new Map
let defaultHarness = ''
const harnessFiles = new Map();
let defaultHarness = '';
for (const entry of fs.readdirSync(harnessDir)) {
if (entry.startsWith('.') || !entry.endsWith('.js')) {
continue
continue;
}
const file = path.join(harnessDir, entry)
const content = fs.readFileSync(file, 'utf8')
const file = path.join(harnessDir, entry);
const content = fs.readFileSync(file, 'utf8');
if (entry === 'assert.js' || entry === 'sta.js') {
defaultHarness += content
continue
defaultHarness += content;
continue;
}
harnessFiles.set(entry, content)
harnessFiles.set(entry, content);
}
function createHarnessForTest(includes) {
let harness = defaultHarness
let harness = defaultHarness;
if (includes) {
for (const include of includes) {
const content = harnessFiles.get(include)
if (!content) throw new Error(`Included file is missing: ${include}`)
harness += content
const content = harnessFiles.get(include);
if (!content) throw new Error(`Included file is missing: ${include}`);
harness += content;
}
}
return harness
return harness;
}
const server = createServer((req, res) => {
@@ -119,16 +118,16 @@ const server = createServer((req, res) => {
body += chunk.toString(); // convert Buffer to string
});
req.on('end', async () => {
const options = JSON.parse(body)
const options = JSON.parse(body);
try {
await runCodeInHarness(options);
} catch (err) {
if (parseInt(process.version.split('.')[0].replace("v", "")) < 20) {
return res.end('Please upgrade the Node.js version to 20 or later.')
if (parseInt(process.version.split('.')[0].replace('v', '')) < 20) {
return res.end('Please upgrade the Node.js version to 20 or later.');
}
return res.end(err.toString());
}
// res.setHeader('Content-Type', 'application/json');
res.end();
});
@@ -140,6 +139,6 @@ const server = createServer((req, res) => {
process.on('unhandledRejection', () => {
// Don't exit when a test does this
})
});
server.listen(32055, () => {});
+46 -48
View File
@@ -1,4 +1,4 @@
const { Linter } = require("eslint");
const { Linter } = require('eslint');
// NOTICE!
// Plugins do not provide their type definitions, and also `@types/*` do not exist!
@@ -13,76 +13,77 @@ const { Linter } = require("eslint");
const {
rules: pluginTypeScriptAllRules,
configs: pluginTypeScriptConfigs,
} = require("@typescript-eslint/eslint-plugin");
} = require('@typescript-eslint/eslint-plugin');
// https://github.com/eslint-community/eslint-plugin-n/blob/master/lib/index.js
const { rules: pluginNAllRules } = require("eslint-plugin-n");
const { rules: pluginNAllRules } = require('eslint-plugin-n');
// https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/index.js
const {
rules: pluginUnicornAllRules,
configs: pluginUnicornConfigs,
} = require("eslint-plugin-unicorn");
} = require('eslint-plugin-unicorn');
// https://github.com/gajus/eslint-plugin-jsdoc/blob/main/src/index.js
const {
// @ts-expect-error: Module has no exported member
rules: pluginJSDocAllRules,
// @ts-expect-error: Module has no exported member
configs: pluginJSDocConfigs,
} = require("eslint-plugin-jsdoc");
} = require('eslint-plugin-jsdoc');
// https://github.com/import-js/eslint-plugin-import/blob/main/src/index.js
const {
rules: pluginImportAllRules,
configs: pluginImportConfigs,
} = require("eslint-plugin-import");
} = require('eslint-plugin-import');
// https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/blob/main/src/index.js
const {
rules: pluginJSXA11yAllRules,
configs: pluginJSXA11yConfigs,
} = require("eslint-plugin-jsx-a11y");
} = require('eslint-plugin-jsx-a11y');
// https://github.com/jest-community/eslint-plugin-jest/blob/main/src/index.ts
const {
rules: pluginJestAllRules,
configs: pluginJestConfigs,
} = require("eslint-plugin-jest");
} = require('eslint-plugin-jest');
// https://github.com/jsx-eslint/eslint-plugin-react/blob/master/index.js
const { rules: pluginReactAllRules } = require("eslint-plugin-react");
const { rules: pluginReactAllRules } = require('eslint-plugin-react');
// https://github.com/facebook/react/blob/main/packages/eslint-plugin-react-hooks/src/index.js
const {
rules: pluginReactHooksAllRules,
} = require("eslint-plugin-react-hooks");
} = require('eslint-plugin-react-hooks');
// https://github.com/cvazac/eslint-plugin-react-perf/blob/master/index.js
const {
rules: pluginReactPerfAllRules,
configs: pluginReactPerfConfigs,
} = require("eslint-plugin-react-perf");
} = require('eslint-plugin-react-perf');
// https://github.com/vercel/next.js/blob/canary/packages/eslint-plugin-next/src/index.ts
const { rules: pluginNextAllRules } = require("@next/eslint-plugin-next");
const { rules: pluginNextAllRules } = require('@next/eslint-plugin-next');
// https://github.com/eslint-community/eslint-plugin-promise/blob/main/index.js
const {
rules: pluginPromiseRules,
configs: pluginPromiseConfigs
} = require("eslint-plugin-promise");
configs: pluginPromiseConfigs,
} = require('eslint-plugin-promise');
// https://github.com/veritem/eslint-plugin-vitest/blob/main/src/index.ts
const {
rules: pluginVitestRules,
configs: pluginVitestConfigs
} = require("eslint-plugin-vitest");
configs: pluginVitestConfigs,
} = require('eslint-plugin-vitest');
/** @param {import("eslint").Linter} linter */
const loadPluginTypeScriptRules = (linter) => {
// We want to list all rules but not support type-checked rules
const pluginTypeScriptDisableTypeCheckedRules = new Map(
Object.entries(pluginTypeScriptConfigs["disable-type-checked"].rules),
Object.entries(pluginTypeScriptConfigs['disable-type-checked'].rules),
);
for (const [name, rule] of Object.entries(pluginTypeScriptAllRules)) {
if (
pluginTypeScriptDisableTypeCheckedRules.has(`@typescript-eslint/${name}`)
)
) {
continue;
}
const prefixedName = `typescript/${name}`;
// Presented but type is `string | false`
rule.meta.docs.recommended = typeof rule.meta.docs.recommended === "string";
rule.meta.docs.recommended = typeof rule.meta.docs.recommended === 'string';
linter.defineRule(prefixedName, rule);
}
@@ -109,7 +110,7 @@ const loadPluginUnicornRules = (linter) => {
// If name is presented and value is not "off", it is recommended
const recommendedValue = pluginUnicornRecommendedRules.get(prefixedName);
// @ts-expect-error: `rule.meta.docs` is possibly `undefined`
rule.meta.docs.recommended = recommendedValue && recommendedValue !== "off";
rule.meta.docs.recommended = recommendedValue && recommendedValue !== 'off';
linter.defineRule(prefixedName, rule);
}
@@ -125,7 +126,7 @@ const loadPluginJSDocRules = (linter) => {
// If name is presented and value is not "off", it is recommended
const recommendedValue = pluginJSDocRecommendedRules.get(prefixedName);
rule.meta.docs.recommended = recommendedValue && recommendedValue !== "off";
rule.meta.docs.recommended = recommendedValue && recommendedValue !== 'off';
linter.defineRule(prefixedName, rule);
}
@@ -157,11 +158,10 @@ const loadPluginJSXA11yRules = (linter) => {
const prefixedName = `jsx-a11y/${name}`;
const recommendedValue = pluginJSXA11yRecommendedRules.get(prefixedName);
rule.meta.docs.recommended =
recommendedValue &&
rule.meta.docs.recommended = recommendedValue &&
// Type is `string | [string, opt]`
recommendedValue !== "off" &&
recommendedValue[0] !== "off";
recommendedValue !== 'off' &&
recommendedValue[0] !== 'off';
linter.defineRule(prefixedName, rule);
}
@@ -178,7 +178,7 @@ const loadPluginJestRules = (linter) => {
const recommendedValue = pluginJestRecommendedRules.get(prefixedName);
// Presented but type is `string | undefined`
rule.meta.docs.recommended = typeof recommendedValue === "string";
rule.meta.docs.recommended = typeof recommendedValue === 'string';
linter.defineRule(prefixedName, rule);
}
@@ -211,8 +211,7 @@ const loadPluginReactPerfRules = (linter) => {
for (const [name, rule] of Object.entries(pluginReactPerfAllRules)) {
const prefixedName = `react-perf/${name}`;
rule.meta.docs.recommended =
pluginReactPerfRecommendedRules.has(prefixedName);
rule.meta.docs.recommended = pluginReactPerfRecommendedRules.has(prefixedName);
linter.defineRule(prefixedName, rule);
}
@@ -235,17 +234,16 @@ const loadPluginPromiseRules = (linter) => {
for (const [name, rule] of Object.entries(pluginPromiseRules)) {
const prefixedName = `promise/${name}`;
rule.meta.docs.recommended =
pluginPromiseRecommendedRules.has(prefixedName);
rule.meta.docs.recommended = pluginPromiseRecommendedRules.has(prefixedName);
linter.defineRule(prefixedName, rule);
}
}
};
/** @param {import("eslint").Linter} linter */
const loadPluginVitestRules = (linter) => {
const pluginVitestRecommendedRules = new Map(
Object.entries(pluginVitestConfigs.recommended.rules)
Object.entries(pluginVitestConfigs.recommended.rules),
);
for (const [name, rule] of Object.entries(pluginVitestRules)) {
const prefixedName = `vitest/${name}`;
@@ -254,7 +252,7 @@ const loadPluginVitestRules = (linter) => {
linter.defineRule(prefixedName, rule);
}
}
};
/**
* @typedef {{
@@ -264,31 +262,31 @@ const loadPluginVitestRules = (linter) => {
* @type {Map<string, TargetPluginMeta>}
*/
exports.ALL_TARGET_PLUGINS = new Map([
["eslint", { npm: ["eslint"], issueNo: 479 }],
["typescript", { npm: ["@typescript-eslint/eslint-plugin"], issueNo: 2180 }],
["n", { npm: ["eslint-plugin-n"], issueNo: 493 }],
["unicorn", { npm: ["eslint-plugin-unicorn"], issueNo: 684 }],
["jsdoc", { npm: ["eslint-plugin-jsdoc"], issueNo: 1170 }],
["import", { npm: ["eslint-plugin-import"], issueNo: 1117 }],
["jsx-a11y", { npm: ["eslint-plugin-jsx-a11y"], issueNo: 1141 }],
["jest", { npm: ["eslint-plugin-jest"], issueNo: 492 }],
['eslint', { npm: ['eslint'], issueNo: 479 }],
['typescript', { npm: ['@typescript-eslint/eslint-plugin'], issueNo: 2180 }],
['n', { npm: ['eslint-plugin-n'], issueNo: 493 }],
['unicorn', { npm: ['eslint-plugin-unicorn'], issueNo: 684 }],
['jsdoc', { npm: ['eslint-plugin-jsdoc'], issueNo: 1170 }],
['import', { npm: ['eslint-plugin-import'], issueNo: 1117 }],
['jsx-a11y', { npm: ['eslint-plugin-jsx-a11y'], issueNo: 1141 }],
['jest', { npm: ['eslint-plugin-jest'], issueNo: 492 }],
[
"react",
'react',
{
npm: ["eslint-plugin-react", "eslint-plugin-react-hooks"],
npm: ['eslint-plugin-react', 'eslint-plugin-react-hooks'],
issueNo: 1022,
},
],
["react-perf", { npm: ["eslint-plugin-react-perf"], issueNo: 2041 }],
["nextjs", { npm: ["@next/eslint-plugin-next"], issueNo: 1929 }],
["promise", { npm: ["eslint-plugin-promise"], issueNo: 4655 }],
["vitest", { npm: ["eslint-plugin-vitest"], issueNo: 4656 }],
['react-perf', { npm: ['eslint-plugin-react-perf'], issueNo: 2041 }],
['nextjs', { npm: ['@next/eslint-plugin-next'], issueNo: 1929 }],
['promise', { npm: ['eslint-plugin-promise'], issueNo: 4655 }],
['vitest', { npm: ['eslint-plugin-vitest'], issueNo: 4656 }],
]);
// All rules(including deprecated, recommended) are loaded initially.
exports.createESLintLinter = () =>
new Linter({
configType: "eslintrc",
configType: 'eslintrc',
});
/** @param {import("eslint").Linter} linter */
+15 -17
View File
@@ -1,18 +1,17 @@
const { parseArgs } = require("node:util");
const { parseArgs } = require('node:util');
const {
ALL_TARGET_PLUGINS,
createESLintLinter,
loadTargetPluginRules,
} = require("./eslint-rules.cjs");
} = require('./eslint-rules.cjs');
const {
createRuleEntries,
updateNotSupportedStatus,
updateImplementedStatus,
overrideTypeScriptPluginStatusWithEslintPluginStatus:
syncTypeScriptPluginStatusWithEslintPluginStatus,
} = require("./oxlint-rules.cjs");
const { renderMarkdown } = require("./markdown-renderer.cjs");
const { updateGitHubIssue } = require("./result-reporter.cjs");
overrideTypeScriptPluginStatusWithEslintPluginStatus: syncTypeScriptPluginStatusWithEslintPluginStatus,
} = require('./oxlint-rules.cjs');
const { renderMarkdown } = require('./markdown-renderer.cjs');
const { updateGitHubIssue } = require('./result-reporter.cjs');
const HELP = `
Usage:
@@ -23,7 +22,7 @@ Options:
--update: Update the issue instead of printing to stdout
--help, -h: Print this help message
Plugins: ${Array.from(ALL_TARGET_PLUGINS.keys()).join(", ")}
Plugins: ${Array.from(ALL_TARGET_PLUGINS.keys()).join(', ')}
`;
(async () => {
@@ -33,9 +32,9 @@ Plugins: ${Array.from(ALL_TARGET_PLUGINS.keys()).join(", ")}
const { values } = parseArgs({
options: {
// Mainly for debugging
target: { type: "string", short: "t", multiple: true },
update: { type: "boolean" },
help: { type: "boolean", short: "h" },
target: { type: 'string', short: 't', multiple: true },
update: { type: 'boolean' },
help: { type: 'boolean', short: 'h' },
},
});
@@ -68,10 +67,9 @@ Plugins: ${Array.from(ALL_TARGET_PLUGINS.keys()).join(", ")}
//
const results = await Promise.allSettled(
Array.from(targetPluginNames).map((pluginName) => {
const pluginMeta =
/** @type {import("./eslint-rules.cjs").TargetPluginMeta} */ (
ALL_TARGET_PLUGINS.get(pluginName)
);
const pluginMeta = /** @type {import("./eslint-rules.cjs").TargetPluginMeta} */ (
ALL_TARGET_PLUGINS.get(pluginName)
);
const content = renderMarkdown(pluginName, pluginMeta, ruleEntries);
if (!values.update) return Promise.resolve(content);
@@ -80,7 +78,7 @@ Plugins: ${Array.from(ALL_TARGET_PLUGINS.keys()).join(", ")}
}),
);
for (const result of results) {
if (result.status === "fulfilled") console.log(result.value);
if (result.status === "rejected") console.error(result.reason);
if (result.status === 'fulfilled') console.log(result.value);
if (result.status === 'rejected') console.error(result.reason);
}
})();
+29 -30
View File
@@ -9,7 +9,7 @@ const renderIntroduction = ({ npm }) => `
> This comment is maintained by CI. Do not edit this comment directly.
> To update comment template, see https://github.com/oxc-project/oxc/tree/main/tasks/lint_rules
This is tracking issue for ${npm.map((n) => "`" + n + "`").join(", ")}.
This is tracking issue for ${npm.map((n) => '`' + n + '`').join(', ')}.
`;
/**
@@ -24,11 +24,9 @@ This is tracking issue for ${npm.map((n) => "`" + n + "`").join(", ")}.
const renderCounters = ({
counters: { recommended, notRecommended, deprecated },
}) => {
const recommendedTodos =
recommended.total -
const recommendedTodos = recommended.total -
(recommended.isImplemented + recommended.isNotSupported);
const notRecommendedTodos =
notRecommended.total -
const notRecommendedTodos = notRecommended.total -
(notRecommended.isImplemented + notRecommended.isNotSupported);
const countersList = [
@@ -38,7 +36,7 @@ const renderCounters = ({
notRecommendedTodos === 0 && ` - All done! 🎉`,
]
.filter(Boolean)
.join("\n");
.join('\n');
return `
There are ${recommended.total + notRecommended.total}(+ ${deprecated.total} deprecated) rules.
@@ -68,19 +66,20 @@ Then register the rule in \`crates/oxc_linter/src/rules.rs\` and also \`declare_
const renderRulesList = ({ title, counters, views, defaultOpen = true }) => `
## ${title}
<details ${defaultOpen ? "open" : ""}>
<details ${defaultOpen ? 'open' : ''}>
<summary>
✨: ${counters.isImplemented}, 🚫: ${counters.isNotSupported} / total: ${counters.total}
</summary>
| Status | Name | Docs |
| :----: | :--- | :--- |
${views
.map(
(v) =>
`| ${v.isImplemented ? "✨" : ""}${v.isNotSupported ? "🚫" : ""} | ${v.name} | ${v.docsUrl} |`,
)
.join("\n")}
${
views
.map(
(v) => `| ${v.isImplemented ? '✨' : ''}${v.isNotSupported ? '🚫' : ''} | ${v.name} | ${v.docsUrl} |`,
)
.join('\n')
}
✨ = Implemented, 🚫 = Not supported
@@ -139,25 +138,25 @@ exports.renderMarkdown = (pluginName, pluginMeta, ruleEntries) => {
renderCounters({ counters }),
renderGettingStarted({ pluginName }),
0 < views.recommended.length &&
renderRulesList({
title: "Recommended rules",
counters: counters.recommended,
views: views.recommended,
}),
renderRulesList({
title: 'Recommended rules',
counters: counters.recommended,
views: views.recommended,
}),
0 < views.notRecommended.length &&
renderRulesList({
title: "Not recommended rules",
counters: counters.notRecommended,
views: views.notRecommended,
}),
renderRulesList({
title: 'Not recommended rules',
counters: counters.notRecommended,
views: views.notRecommended,
}),
0 < views.deprecated.length &&
renderRulesList({
title: "Deprecated rules",
counters: counters.deprecated,
views: views.deprecated,
defaultOpen: false,
}),
renderRulesList({
title: 'Deprecated rules',
counters: counters.deprecated,
views: views.deprecated,
defaultOpen: false,
}),
]
.filter(Boolean)
.join("\n");
.join('\n');
};
+22 -22
View File
@@ -1,52 +1,52 @@
const { resolve } = require("node:path");
const { readFile } = require("node:fs/promises");
const { resolve } = require('node:path');
const { readFile } = require('node:fs/promises');
const readAllImplementedRuleNames = async () => {
const rulesFile = await readFile(
resolve("crates/oxc_linter/src/rules.rs"),
"utf8",
resolve('crates/oxc_linter/src/rules.rs'),
'utf8',
);
/** @type {Set<string>} */
const rules = new Set();
let found = false;
for (let line of rulesFile.split("\n")) {
for (let line of rulesFile.split('\n')) {
line = line.trim();
// Skip commented out rules
if (line.startsWith("//")) continue;
if (line.startsWith('//')) continue;
if (line === "oxc_macros::declare_all_lint_rules! {") {
if (line === 'oxc_macros::declare_all_lint_rules! {') {
found = true;
continue;
}
if (found && line === "}") {
if (found && line === '}') {
return rules;
}
if (found) {
const prefixedName = line
.replaceAll(",", "")
.replaceAll("::", "/")
.replaceAll("_", "-");
.replaceAll(',', '')
.replaceAll('::', '/')
.replaceAll('_', '-');
// Ignore no reference rules
if (prefixedName.startsWith("oxc/")) continue;
if (prefixedName.startsWith('oxc/')) continue;
rules.add(prefixedName);
}
}
throw new Error("Failed to find the end of the rules list");
throw new Error('Failed to find the end of the rules list');
};
const NOT_SUPPORTED_RULE_NAMES = new Set([
"eslint/no-dupe-args", // superseded by strict mode
"eslint/no-octal", // superseded by strict mode
"eslint/no-with", // superseded by strict mode
"eslint/no-new-symbol", // Deprecated as of ESLint v9, but for a while disable manually
"import/no-unresolved", // Will always contain false positives due to module resolution complexity
'eslint/no-dupe-args', // superseded by strict mode
'eslint/no-octal', // superseded by strict mode
'eslint/no-with', // superseded by strict mode
'eslint/no-new-symbol', // Deprecated as of ESLint v9, but for a while disable manually
'import/no-unresolved', // Will always contain false positives due to module resolution complexity
]);
/**
@@ -67,9 +67,9 @@ exports.createRuleEntries = (loadedAllRules) => {
for (const [name, rule] of loadedAllRules) {
// Default eslint rules are not prefixed
const prefixedName = name.includes("/") ? name : `eslint/${name}`;
const prefixedName = name.includes('/') ? name : `eslint/${name}`;
const docsUrl = rule.meta?.docs?.url ?? "";
const docsUrl = rule.meta?.docs?.url ?? '';
const isDeprecated = rule.meta?.deprecated ?? false;
const isRecommended = rule.meta?.docs?.recommended ?? false;
@@ -117,10 +117,10 @@ exports.overrideTypeScriptPluginStatusWithEslintPluginStatus = (
ruleEntries,
) => {
for (const [name, rule] of ruleEntries) {
if (!name.startsWith("typescript/")) continue;
if (!name.startsWith('typescript/')) continue;
// This assumes that if the same name found, it implements the same rule.
const eslintRule = ruleEntries.get(name.replace("typescript/", "eslint/"));
const eslintRule = ruleEntries.get(name.replace('typescript/', 'eslint/'));
if (!eslintRule) continue;
rule.isImplemented = eslintRule.isImplemented;
+2 -2
View File
@@ -9,9 +9,9 @@ exports.updateGitHubIssue = async ({ issueNo }, markdown) => {
const res = await fetch(
`https://api.github.com/repos/oxc-project/oxc/issues/${issueNo}`,
{
method: "PATCH",
method: 'PATCH',
headers: {
Accept: "application/vnd.github+json",
Accept: 'application/vnd.github+json',
Authorization: `Bearer ${process.env.GITHUB_TOKEN}`,
},
body: JSON.stringify({ body: markdown }),
+10 -10
View File
@@ -4,8 +4,8 @@ Experimental wasm package for the oxc parser, with full TypeScript typings suppo
This package is built with different [wasm-pack's target](https://rustwasm.github.io/docs/wasm-bindgen/reference/deployment.html) builds:
* `wasm-pack build --target web` for bundler (webpack / vite) consumption.
* `wasm-pack build --target nodejs` for node.js
- `wasm-pack build --target web` for bundler (webpack / vite) consumption.
- `wasm-pack build --target nodejs` for node.js
And exports the files as
@@ -22,13 +22,13 @@ Source code: https://github.com/oxc-project/oxc/tree/main/wasm/parser
## Usage
```js
import initWasm, { parseSync } from "@oxc-parser/wasm";
import initWasm, { parseSync } from '@oxc-parser/wasm';
async function main() {
await initWasm();
const code = "let foo";
const result = parseSync(code, { sourceFilename: "test.ts" });
const code = 'let foo';
const result = parseSync(code, { sourceFilename: 'test.ts' });
console.log(result);
}
@@ -46,13 +46,13 @@ let sourceTextUtf8 = new TextEncoder().encode(sourceText);
const convertToUtf8 = (sourceTextUtf8, d) => {
return new TextDecoder().decode(sourceTextUtf8.slice(0, d)).length;
}
};
const diagnostics = result.errors.map((d) => ({
from: convertToUtf8(sourceTextUtf8, d.start),
to: convertToUtf8(sourceTextUtf8, d.end),
severity: d.severity.toLowerCase(),
message: d.message,
from: convertToUtf8(sourceTextUtf8, d.start),
to: convertToUtf8(sourceTextUtf8, d.end),
severity: d.severity.toLowerCase(),
message: d.message,
}));
```
+4 -4
View File
@@ -1,7 +1,7 @@
import assert from 'assert'
import { parseSync } from "../../npm/parser-wasm/node/oxc_parser_wasm.js";
import assert from 'assert';
import { parseSync } from '../../npm/parser-wasm/node/oxc_parser_wasm.js';
const code = "let foo";
const result = parseSync(code, { sourceFilename: "test.ts" });
const code = 'let foo';
const result = parseSync(code, { sourceFilename: 'test.ts' });
assert(result.errors.length === 0);
assert(result.program.body.length === 1);