Spaces:
Build error
Build error
| import { parseFiles } from "@ast-grep/napi"; | |
| import MagicString from "magic-string"; | |
| import { chalk, fs, path } from "zx"; | |
| import { errors } from "./errors.js"; | |
| import { root } from "./utils.js"; | |
| /** | |
| * @typedef {import("@ast-grep/napi").SgNode} SgNode | |
| */ | |
| const export_lentgh = "export".length; | |
| export function ast_grep() { | |
| const task_queue = []; | |
| const task = parseFiles([root("esm")], (err, tree) => { | |
| const filename = path.basename(tree.filename(), ".js"); | |
| if (filename === "index") { | |
| return; | |
| } | |
| const source = new MagicString(tree.root().text()); | |
| source.prepend(`"use strict";\n\n`); | |
| if (filename.startsWith("_ts")) { | |
| const match = tree.root().find(`export { $NAME as _, $NAME as $ALIAS } from "tslib"`); | |
| if (match) { | |
| const name = match.getMatch("NAME").text(); | |
| const alias = match.getMatch("ALIAS").text(); | |
| if (alias !== filename) { | |
| report_ts_mismatch(tree.filename(), match); | |
| } | |
| const range = match.range(); | |
| source.update( | |
| range.start.index, | |
| range.end.index, | |
| `exports._ = exports.${alias} = require("tslib").${name};`, | |
| ); | |
| task_queue.push( | |
| fs.writeFile(root("cjs", `${filename}.cjs`), source.toString(), { | |
| encoding: "utf-8", | |
| }), | |
| ); | |
| } else { | |
| report_noexport(tree.filename()); | |
| } | |
| return; | |
| } | |
| // rewrite export named function | |
| const match = tree.root().find({ | |
| rule: { | |
| kind: "export_statement", | |
| pattern: "export function $FUNC($$$){$$$}", | |
| }, | |
| }); | |
| if (match) { | |
| const func = match.getMatch("FUNC"); | |
| const func_name = func.text(); | |
| if (func_name !== filename) { | |
| report_export_mismatch(tree.filename(), match); | |
| } | |
| const export_start = match.range().start.index; | |
| const export_end = export_start + export_lentgh; | |
| source.update( | |
| export_start, | |
| export_end, | |
| `exports._ = exports.${func_name} = ${func_name};`, | |
| ); | |
| match | |
| .findAll({ | |
| rule: { | |
| pattern: func_name, | |
| kind: "identifier", | |
| inside: { kind: "assignment_expression", field: "left" }, | |
| }, | |
| }) | |
| .forEach((match) => { | |
| const range = match.range(); | |
| source.prependLeft(range.start.index, `exports._ = exports.${func_name} = `); | |
| }); | |
| const export_shortname = `export { ${func_name} as _}`; | |
| const export_alias = tree.root().find(export_shortname); | |
| if (!export_alias) { | |
| task_queue.push( | |
| fs.appendFile(tree.filename(), export_shortname, "utf-8"), | |
| ); | |
| } else { | |
| const range = export_alias.range(); | |
| source.remove(range.start.index, range.end.index); | |
| } | |
| } else { | |
| report_noexport(tree.filename(tree.filename())); | |
| } | |
| // rewrite import | |
| tree | |
| .root() | |
| .findAll({ rule: { pattern: `import { $BINDING } from "$SOURCE"` } }) | |
| .forEach((match) => { | |
| const import_binding = match.getMatch("BINDING").text(); | |
| const import_source = match.getMatch("SOURCE").text(); | |
| const import_basename = path.basename(import_source, ".js"); | |
| if (import_binding !== import_basename) { | |
| report_import_mismatch(tree.filename(), match); | |
| } | |
| const range = match.range(); | |
| source.update( | |
| range.start.index, | |
| range.end.index, | |
| `var ${import_binding} = require("./${import_binding}.cjs");`, | |
| ); | |
| tree | |
| .root() | |
| .findAll({ | |
| rule: { | |
| pattern: import_binding, | |
| kind: "identifier", | |
| inside: { | |
| not: { | |
| kind: "import_specifier", | |
| }, | |
| }, | |
| }, | |
| }) | |
| .forEach((match) => { | |
| const range = match.range(); | |
| const ref_name = match.text(); | |
| source.update( | |
| range.start.index, | |
| range.end.index, | |
| `${ref_name}._`, | |
| ); | |
| }); | |
| }); | |
| task_queue.push( | |
| fs.writeFile(root("cjs", `${filename}.cjs`), source.toString(), { | |
| encoding: "utf-8", | |
| }), | |
| ); | |
| }); | |
| task_queue.push(task); | |
| return task_queue; | |
| } | |
| /** | |
| * @param {string} filename | |
| * @param {SgNode} match | |
| */ | |
| function report_ts_mismatch(filename, match) { | |
| const range = match.getMatch("ALIAS").range(); | |
| errors.push( | |
| [ | |
| `${chalk.bold.red("error")}: mismatch exported function name.`, | |
| "", | |
| `${chalk.blue("-->")} ${filename}:${match.range().start.line + 1}`, | |
| "", | |
| match.text(), | |
| chalk.red( | |
| [ | |
| " ".repeat(range.start.column), | |
| "^".repeat(range.end.column - range.start.column), | |
| ] | |
| .join(""), | |
| ), | |
| `${ | |
| chalk.bold( | |
| "note:", | |
| ) | |
| } The exported name should be the same as the filename.`, | |
| "", | |
| ] | |
| .join("\n"), | |
| ); | |
| } | |
| /** | |
| * @param {string} filename | |
| * @param {SgNode} match | |
| */ | |
| function report_export_mismatch(filename, match) { | |
| const func = match.getMatch("FUNC"); | |
| const func_range = func.range(); | |
| const text = match.text().split("\n"); | |
| const offset = func_range.start.line - match.range().start.line; | |
| text.splice( | |
| offset + 1, | |
| text.length, | |
| chalk.red( | |
| [ | |
| " ".repeat(func_range.start.column), | |
| "^".repeat(func_range.end.column - func_range.start.column), | |
| ] | |
| .join(""), | |
| ), | |
| ); | |
| errors.push( | |
| [ | |
| `${chalk.bold.red("error")}: mismatch exported function name.`, | |
| "", | |
| `${chalk.blue("-->")} ${filename}:${func_range.start.line + 1}:${func_range.start.column + 1}`, | |
| "", | |
| ...text, | |
| "", | |
| `${ | |
| chalk.bold( | |
| "note:", | |
| ) | |
| } The exported name should be the same as the filename.`, | |
| "", | |
| ] | |
| .join("\n"), | |
| ); | |
| } | |
| /** | |
| * @param {string} filename | |
| * @param {SgNode} match | |
| */ | |
| function report_import_mismatch(filename, match) { | |
| const binding_range = match.getMatch("BINDING").range(); | |
| const source_range = match.getMatch("SOURCE").range(); | |
| errors.push( | |
| [ | |
| `${chalk.bold.red("error")}: mismatch imported binding name.`, | |
| "", | |
| `${chalk.blue("-->")} ${filename}:${match.range().start.line + 1}`, | |
| "", | |
| match.text(), | |
| [ | |
| " ".repeat(binding_range.start.column), | |
| chalk.red("^".repeat(binding_range.end.column - binding_range.start.column)), | |
| " ".repeat(source_range.start.column - binding_range.end.column), | |
| chalk.blue("-".repeat(source_range.end.column - source_range.start.column)), | |
| ] | |
| .join(""), | |
| `${ | |
| chalk.bold( | |
| "note:", | |
| ) | |
| } The imported binding name should be the same as the import source basename.`, | |
| "", | |
| ] | |
| .join("\n"), | |
| ); | |
| } | |
| /** | |
| * @param {string} filename | |
| */ | |
| function report_noexport(filename) { | |
| errors.push( | |
| [`${chalk.bold.red("error")}: exported name not found`, `${chalk.blue("-->")} ${filename}`].join("\n"), | |
| ); | |
| } | |