@sdk-usage/core
Version:
sdk-usage core functionalities
246 lines (238 loc) • 7.2 kB
JavaScript
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
let node_fs = require("node:fs");
let node_path = require("node:path");
let node_child_process = require("node:child_process");
let node_module = require("node:module");
let _open_vanilla_visitor = require("@open-vanilla/visitor");
let _swc_core = require("@swc/core");
let fdir = require("fdir");
//#region src/entities/item/createLocation.ts
const createLocation = ({ code, file, link, module, offset, path }) => {
const linesTillOffset = code.slice(0, Math.max(0, offset)).split(/\n/);
const line = linesTillOffset.length;
return {
column: linesTillOffset[line - 1].length,
file: `./${(0, node_path.relative)(path, file)}`,
line,
link,
module
};
};
//#endregion
//#region src/entities/item/createItem.ts
/**
* Aggregate factory that creates an item.
* @param input - Factory variables.
* @param input.name - Name.
* @param input.type - Component type.
* @param input.module - Source module.
* @param input.version - Module version.
* @param input.location - Location.
* @param input.input - Parameter list and metadata describing the way it's passed.
* @returns Created item.
* @example
* createItem({ ... });
*/
const createItem = ({ input, location, module, name, type, version }) => {
return {
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
input: {
data: input?.data ?? {},
metadata: { withSpreading: input?.metadata.withSpreading ?? false }
},
location: createLocation(location),
module,
name,
type,
version
};
};
//#endregion
//#region src/helpers.ts
const require$1 = (0, node_module.createRequire)(require("url").pathToFileURL(__filename).href);
const resolvePackageJson = (fromPath) => {
const filepath = (0, node_path.join)(fromPath, "./package.json");
if ((0, node_fs.existsSync)(filepath)) return filepath;
return resolvePackageJson((0, node_path.join)(fromPath, "../"));
};
/**
* Execute an external command.
* @param command - The command to execute.
* @param options - Options including current working directory configuration.
* @param options.cwd - Configure the current working directory.
* @returns The output (either the command output or error).
* @example
* exec("ls");
*/
const exec = async (command, options = {}) => {
return new Promise((resolve, reject) => {
let stdout = "";
let stderr = "";
const childProcess = (0, node_child_process.spawn)(command, {
cwd: options.cwd,
shell: true,
stdio: "pipe"
});
childProcess.stdout.on("data", (chunk) => {
stdout += chunk;
});
childProcess.stderr.on("data", (chunk) => {
stderr += chunk;
});
childProcess.on("close", (exitCode) => {
if (exitCode === 0) resolve(stdout.trim());
else {
const output = `${stderr}${stdout}`;
reject(new Error(output.trim()));
}
});
});
};
//#endregion
//#region src/modules/parser/parse.ts
const parse = async (code, { onAdd, plugins }) => {
const context = { imports: /* @__PURE__ */ new Map() };
let ast;
try {
ast = await (0, _swc_core.parse)(code, {
syntax: "typescript",
tsx: true
});
} catch {
ast = void 0;
}
if (ast === void 0) return;
const visitor = { ImportDeclaration(node) {
const module = node.source.value;
node.specifiers.forEach((specifier) => {
const specifierValue = specifier.local.value;
context.imports.set(specifierValue, {
alias: specifierValue,
module,
name: specifier.imported?.value ?? specifierValue
});
});
} };
for (const plugin of plugins) {
const pluginOutput = plugin(context, { getJSXAttributeValue });
const nodeKeys = Object.keys(pluginOutput);
for (const nodeKey of nodeKeys) {
const currentVisitorFunction = visitor[nodeKey];
visitor[nodeKey] = (node) => {
if (typeof currentVisitorFunction === "function") currentVisitorFunction(node);
const output = pluginOutput[nodeKey]?.(node);
if (output) onAdd(output);
};
}
}
(0, _open_vanilla_visitor.visit)(ast, visitor);
};
const getJSXAttributeValue = (node) => {
if (!node) return true;
switch (node.type) {
case "BigIntLiteral":
case "BooleanLiteral":
case "JSXText":
case "NumericLiteral":
case "StringLiteral": return node.value;
case "JSXExpressionContainer": return getJSXAttributeValue(node.expression);
case "NullLiteral": return null;
default: return createUnknownToken(node.type);
}
};
/**
* Helper to unify the way unknown AST token are managed.
* @param token - AST token value.
* @returns Formatted AST token.
* @example
* createUnknownToken("VariableDeclaration");
*/
const createUnknownToken = (token) => `#${token}`;
//#endregion
//#region src/modules/scanner/scan.ts
const scan = async (path, options = {}) => {
const excludedFolders = options.excludeFolders ?? DEFAULT_EXCLUDED_FOLDERS;
const includedFiles = options.includeFiles ?? DEFAULT_INCLUDED_FILES;
const projectPaths = new fdir.fdir().withBasePath().glob("**/package.json").exclude((directoryName) => excludedFolders.includes(directoryName)).crawl(path).sync();
const projects = [];
for (const projectPath of projectPaths) {
const metadata = require$1(projectPath);
const folder = (0, node_path.dirname)(projectPath);
let link;
try {
link = await exec("git config --get remote.origin.url", { cwd: folder });
} catch {
link = "";
}
projects.push({
folder,
link,
metadata
});
}
return projects.map((project) => {
const files = new fdir.fdir().withBasePath().glob(...includedFiles).exclude((directoryName) => excludedFolders.includes(directoryName)).crawl(project.folder).sync();
return {
...project,
files
};
});
};
const DEFAULT_EXCLUDED_FOLDERS = [
".git",
"node_modules",
"dist",
"out"
];
const DEFAULT_INCLUDED_FILES = ["**/*/!(test|*.test|stories|*.stories).?(m){j,t}s?(x)"];
//#endregion
//#region src/createInstance.ts
const createInstance = (path, options) => {
return { async getItems() {
const projects = await scan(path);
const items = [];
for (const project of projects) {
const module = project.metadata.name;
const dependencies = {
...project.metadata.devDependencies,
...project.metadata.optionalDependencies,
...project.metadata.dependencies
};
const link = project.link;
for (const file of project.files) {
const code = (0, node_fs.readFileSync)(file, "utf8");
await parse(code, {
onAdd(item) {
if (options.includeModules && options.includeModules.length > 0 && !options.includeModules.includes(item.module)) return;
let version = dependencies[item.module] ?? "";
if (options.resolveInstalledVersions) try {
version = require$1(resolvePackageJson(require$1.resolve(item.module, { paths: [file] }))).version;
} catch {}
items.push(createItem({
...item,
location: {
code,
file,
link,
module,
offset: item.offset,
path
},
version
}));
},
plugins: options.plugins ?? []
});
}
}
return items;
} };
};
//#endregion
//#region src/modules/plugin/createPlugin.ts
const createPlugin = (input) => {
return input;
};
//#endregion
exports.createInstance = createInstance;
exports.createPlugin = createPlugin;