Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion apps/oxlint/src-js/bindings.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,10 @@ export type JsDestroyWorkspaceCb =
export type JsLintFileCb =
((arg0: string, arg1: number, arg2: Uint8Array | undefined | null, arg3: Array<number>, arg4: Array<number>, arg5: string, arg6: string) => string | null)

/** JS callback to load JavaScript config files. */
export type JsLoadJsConfigsCb =
((arg: Array<string>) => Promise<string>)

/** JS callback to load a JS plugin. */
export type JsLoadPluginCb =
((arg0: string, arg1: string | undefined | null, arg2: boolean) => Promise<string>)
Expand All @@ -64,10 +68,11 @@ export type JsSetupRuleConfigsCb =
* 4. `lint_file`: Lint a file.
* 5. `create_workspace`: Create a workspace.
* 6. `destroy_workspace`: Destroy a workspace.
* 7. `load_js_configs`: Load JavaScript config files.
*
* Returns `true` if linting succeeded without errors, `false` otherwise.
*/
export declare function lint(args: Array<string>, loadPlugin: JsLoadPluginCb, setupRuleConfigs: JsSetupRuleConfigsCb, lintFile: JsLintFileCb, createWorkspace: JsCreateWorkspaceCb, destroyWorkspace: JsDestroyWorkspaceCb): Promise<boolean>
export declare function lint(args: Array<string>, loadPlugin: JsLoadPluginCb, setupRuleConfigs: JsSetupRuleConfigsCb, lintFile: JsLintFileCb, createWorkspace: JsCreateWorkspaceCb, destroyWorkspace: JsDestroyWorkspaceCb, loadJsConfigs: JsLoadJsConfigsCb): Promise<boolean>

/**
* Parse AST into provided `Uint8Array` buffer, synchronously.
Expand Down
24 changes: 22 additions & 2 deletions apps/oxlint/src-js/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ let setupRuleConfigs: typeof import("./plugins/index.ts").setupRuleConfigs | nul
let lintFile: typeof import("./plugins/index.ts").lintFile | null = null;
let createWorkspace: typeof import("./workspace/index.ts").createWorkspace | null = null;
let destroyWorkspace: typeof import("./workspace/index.ts").destroyWorkspace | null = null;
// Lazy-loaded JS/TS config loader (experimental)
let loadJsConfigs: typeof import("./js_config.ts").loadJsConfigs | null = null;

/**
* Load a plugin.
Expand Down Expand Up @@ -114,19 +116,37 @@ function destroyWorkspaceWrapper(workspace: string): undefined {
debugAssertIsNonNull(destroyWorkspace);
destroyWorkspace(workspace);
}
/**
* Load JavaScript/TypeScript config files (experimental).
*
* Lazy-loads the js_config module on first call.
* Uses native Node.js TypeScript support to import config files.
*
* @param paths - Array of absolute paths to oxlint.config.ts files
* @returns JSON-stringified result with all configs or error
*/
function loadJsConfigsWrapper(paths: string[]): Promise<string> {
if (loadJsConfigs === null) {
return import("./js_config.ts").then((mod) => {
loadJsConfigs = mod.loadJsConfigs;
return loadJsConfigs(paths);
});
}
return loadJsConfigs(paths);
}

// Get command line arguments, skipping first 2 (node binary and script path)
const args = process.argv.slice(2);

// Call Rust, passing `loadPlugin`, `setupRuleConfigs`, `lintFile`, `createWorkspace`, and `destroyWorkspace`
// as callbacks, and CLI arguments
// Call Rust, passing callbacks and CLI arguments
const success = await lint(
args,
loadPluginWrapper,
setupRuleConfigsWrapper,
lintFileWrapper,
createWorkspaceWrapper,
destroyWorkspaceWrapper,
loadJsConfigsWrapper,
);

// Note: It's recommended to set `process.exitCode` instead of calling `process.exit()`.
Expand Down
12 changes: 12 additions & 0 deletions apps/oxlint/src-js/js_config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/**
* Load JavaScript config files in parallel.
*
* Uses native Node.js TypeScript support to import the config files.
* Each config file should have a default export containing the oxlint configuration.
*
* @param paths - Array of absolute paths to oxlint.config.ts files
* @returns JSON-stringified result with all configs or error
*/
export async function loadJsConfigs(_paths: string[]): Promise<string> {
throw new Error("Not implemented yet");
}
64 changes: 50 additions & 14 deletions apps/oxlint/src/run.rs
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,21 @@ pub type JsSetupRuleConfigsCb = ThreadsafeFunction<
false,
>;

/// JS callback to load JavaScript config files.
#[napi]
pub type JsLoadJsConfigsCb = ThreadsafeFunction<
// Arguments: Vec of absolute paths to oxlint.config.ts files
Vec<String>,
// Return value: JSON string containing success/failure result
Promise<String>,
// Arguments (repeated)
Vec<String>,
// Error status
Status,
// CalleeHandled
false,
>;

/// NAPI entry point.
///
/// JS side passes in:
Expand All @@ -116,6 +131,7 @@ pub type JsSetupRuleConfigsCb = ThreadsafeFunction<
/// 4. `lint_file`: Lint a file.
/// 5. `create_workspace`: Create a workspace.
/// 6. `destroy_workspace`: Destroy a workspace.
/// 7. `load_js_configs`: Load JavaScript config files.
///
/// Returns `true` if linting succeeded without errors, `false` otherwise.
#[expect(clippy::allow_attributes)]
Expand All @@ -128,10 +144,19 @@ pub async fn lint(
lint_file: JsLintFileCb,
create_workspace: JsCreateWorkspaceCb,
destroy_workspace: JsDestroyWorkspaceCb,
load_js_configs: JsLoadJsConfigsCb,
) -> bool {
lint_impl(args, load_plugin, setup_rule_configs, lint_file, create_workspace, destroy_workspace)
.await
.report()
lint_impl(
args,
load_plugin,
setup_rule_configs,
lint_file,
create_workspace,
destroy_workspace,
load_js_configs,
)
.await
.report()
== ExitCode::SUCCESS
}

Expand All @@ -143,6 +168,7 @@ async fn lint_impl(
lint_file: JsLintFileCb,
create_workspace: JsCreateWorkspaceCb,
destroy_workspace: JsDestroyWorkspaceCb,
load_js_configs: JsLoadJsConfigsCb,
) -> CliRunResult {
// Convert String args to OsString for compatibility with bpaf
let args: Vec<std::ffi::OsString> = args.into_iter().map(std::ffi::OsString::from).collect();
Expand All @@ -167,18 +193,28 @@ async fn lint_impl(

// JS plugins are only supported on 64-bit little-endian platforms at present
#[cfg(all(target_pointer_width = "64", target_endian = "little"))]
let external_linter = Some(crate::js_plugins::create_external_linter(
load_plugin,
setup_rule_configs,
lint_file,
create_workspace,
destroy_workspace,
));
let (external_linter, _) = {
let js_config_loader = Some(load_js_configs);
let external_linter = Some(crate::js_plugins::create_external_linter(
load_plugin,
setup_rule_configs,
lint_file,
create_workspace,
destroy_workspace,
));
(external_linter, js_config_loader)
};
#[cfg(not(all(target_pointer_width = "64", target_endian = "little")))]
let external_linter = {
let (_, _, _, _, _) =
(load_plugin, setup_rule_configs, lint_file, create_workspace, destroy_workspace);
None
let (external_linter, js_config_loader) = {
let (_, _, _, _, _) = (
load_plugin,
setup_rule_configs,
lint_file,
create_workspace,
destroy_workspace,
load_js_configs,
);
(None, None)
};

// If --lsp flag is set, run the language server
Expand Down
Loading