@zhin.js/dependency 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +769 -0
- package/dist/dependency.d.ts +42 -0
- package/dist/dependency.d.ts.map +1 -0
- package/dist/dependency.js +295 -0
- package/dist/dependency.js.map +1 -0
- package/dist/hook-registry.d.ts +37 -0
- package/dist/hook-registry.d.ts.map +1 -0
- package/dist/hook-registry.js +75 -0
- package/dist/hook-registry.js.map +1 -0
- package/dist/hooks.d.ts +6 -0
- package/dist/hooks.d.ts.map +1 -0
- package/dist/hooks.js +44 -0
- package/dist/hooks.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -0
- package/dist/types.d.ts +4 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/loaders/bun-plugin.ts +86 -0
- package/loaders/bun-preload.ts +16 -0
- package/loaders/register.mjs +18 -0
- package/loaders/transform-utils.mjs +108 -0
- package/loaders/tsx-loader.mjs +68 -0
- package/package.json +45 -0
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bun Plugin
|
|
3
|
+
* 在 Bun 运行时拦截模块加载,自动转换 import 语句
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { BunPlugin } from 'bun';
|
|
7
|
+
import { readFileSync } from 'fs';
|
|
8
|
+
import { transformImports, hasRelativeImports, shouldTransformPath } from './transform-utils.mjs';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* 创建 Bun Plugin 配置
|
|
12
|
+
* 使用环境变量配置:
|
|
13
|
+
* - DEPENDENCY_TREE_INCLUDE: 需要处理的路径(支持 node_modules)
|
|
14
|
+
* - DEPENDENCY_TREE_EXCLUDE: 需要排除的路径
|
|
15
|
+
*/
|
|
16
|
+
export function createDependencyTreePlugin(): BunPlugin {
|
|
17
|
+
return {
|
|
18
|
+
name: 'dependency-tree-plugin',
|
|
19
|
+
|
|
20
|
+
setup(build) {
|
|
21
|
+
// 根据环境变量动态构建 filter,避免不必要的文件读取
|
|
22
|
+
const includePaths = process.env.DEPENDENCY_TREE_INCLUDE;
|
|
23
|
+
|
|
24
|
+
let filter: RegExp;
|
|
25
|
+
if (includePaths) {
|
|
26
|
+
// 如果设置了 INCLUDE,构建包含这些路径的 filter
|
|
27
|
+
const paths = includePaths.split(',').map(p => p.trim());
|
|
28
|
+
const patterns = paths.map(p => p.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')).join('|');
|
|
29
|
+
filter = new RegExp(`(${patterns}).*\\.(ts|js)$`);
|
|
30
|
+
} else {
|
|
31
|
+
// 默认:只处理非 node_modules 的文件
|
|
32
|
+
filter = /^(?!.*\/node_modules\/).*\.(ts|js)$/;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
build.onLoad({ filter }, async (args) => {
|
|
36
|
+
const source = readFileSync(args.path, 'utf-8');
|
|
37
|
+
|
|
38
|
+
// 二次检查:使用统一的判断逻辑(支持 EXCLUDE)
|
|
39
|
+
if (!shouldTransformPath(args.path, ['.ts', '.js'])) {
|
|
40
|
+
// 不需要处理此文件,返回原始内容
|
|
41
|
+
return {
|
|
42
|
+
contents: source,
|
|
43
|
+
loader: 'ts',
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// 检查是否是热重载(URL 包含时间戳查询参数)
|
|
48
|
+
// 如果是热重载,强制重新转换以重新注册 hooks
|
|
49
|
+
const isHotReload = args.path.includes('?');
|
|
50
|
+
|
|
51
|
+
// 检查是否已经转换过或没有相对路径的 import
|
|
52
|
+
const alreadyTransformed = source.includes('__BUN_PLUGIN_TRANSFORMED__');
|
|
53
|
+
|
|
54
|
+
// 如果不是热重载,且已经转换过或没有相对导入,跳过转换
|
|
55
|
+
if (!isHotReload && (alreadyTransformed || !hasRelativeImports(source))) {
|
|
56
|
+
// 返回原始内容,不进行转换
|
|
57
|
+
return {
|
|
58
|
+
contents: source,
|
|
59
|
+
loader: 'ts',
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// 转换相对路径的 import 语句
|
|
64
|
+
const transformed = transformImports(source, args.path, isHotReload, '__BUN_PLUGIN_TRANSFORMED__');
|
|
65
|
+
|
|
66
|
+
return {
|
|
67
|
+
contents: transformed,
|
|
68
|
+
loader: 'ts',
|
|
69
|
+
};
|
|
70
|
+
});
|
|
71
|
+
},
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* 默认的 Bun Plugin(保持向后兼容)
|
|
77
|
+
*/
|
|
78
|
+
export const dependencyTreePlugin = createDependencyTreePlugin();
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* 注册 Bun Plugin
|
|
82
|
+
*/
|
|
83
|
+
export function registerBunPlugin() {
|
|
84
|
+
// Bun 会自动识别并加载此插件
|
|
85
|
+
return dependencyTreePlugin;
|
|
86
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bun Preload Script
|
|
3
|
+
* 在 Bun 启动时注册插件
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { plugin } from 'bun';
|
|
7
|
+
import { createDependencyTreePlugin } from './bun-plugin';
|
|
8
|
+
|
|
9
|
+
// 创建并注册插件
|
|
10
|
+
// 可以通过环境变量 DEPENDENCY_TREE_INCLUDE 配置需要处理的路径
|
|
11
|
+
// 例如: DEPENDENCY_TREE_INCLUDE="/plugins/,/src/modules/" bun run index.ts
|
|
12
|
+
const dependencyTreePlugin = createDependencyTreePlugin();
|
|
13
|
+
plugin(dependencyTreePlugin);
|
|
14
|
+
|
|
15
|
+
console.log('✅ Bun plugin registered');
|
|
16
|
+
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Universal Loader Registration
|
|
3
|
+
* 适用于 Node.js 和 tsx
|
|
4
|
+
* 使用新的 register() API 注册 loader
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { register } from 'node:module';
|
|
8
|
+
import { pathToFileURL } from 'node:url';
|
|
9
|
+
import { fileURLToPath } from 'node:url';
|
|
10
|
+
import { dirname, join } from 'node:path';
|
|
11
|
+
|
|
12
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
13
|
+
const __dirname = dirname(__filename);
|
|
14
|
+
|
|
15
|
+
// 注册通用 loader(tsx-loader 同时支持 .ts 和 .js,适用于两种环境)
|
|
16
|
+
const loaderPath = join(__dirname, 'tsx-loader.mjs');
|
|
17
|
+
register(pathToFileURL(loaderPath), import.meta.url);
|
|
18
|
+
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import pkgJson from '../package.json' with { type: 'json' };
|
|
2
|
+
/**
|
|
3
|
+
* 通用转换工具
|
|
4
|
+
* 供 tsx-loader 和 bun-plugin 共享使用
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* 检查是否有相对路径的 import
|
|
9
|
+
*/
|
|
10
|
+
export function hasRelativeImports(source) {
|
|
11
|
+
return /^import\s+['"](\.[^'"]+)['"]/m.test(source);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* 转换 import 语句
|
|
16
|
+
* @param {string} source - 源代码
|
|
17
|
+
* @param {string} currentPath - 当前文件路径
|
|
18
|
+
* @param {boolean} isHotReload - 是否为热重载
|
|
19
|
+
* @param {string} marker - 转换标记(如 '__LOADER_TRANSFORMED__' 或 '__BUN_PLUGIN_TRANSFORMED__')
|
|
20
|
+
*/
|
|
21
|
+
export function transformImports(source, currentPath, isHotReload = false, marker = '__LOADER_TRANSFORMED__') {
|
|
22
|
+
// 检查是否有相对路径的 import
|
|
23
|
+
if (!hasRelativeImports(source) && !isHotReload) {
|
|
24
|
+
return source;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// 添加标记(热重载时添加时间戳)
|
|
28
|
+
let result = isHotReload
|
|
29
|
+
? `/* ${marker} (Hot Reload: ${Date.now()}) */\n`
|
|
30
|
+
: `/* ${marker} */\n`;
|
|
31
|
+
|
|
32
|
+
// 检查是否已有 importModule 导入
|
|
33
|
+
const hasImportModule = /import.*importModule.*from.*hooks/.test(source);
|
|
34
|
+
|
|
35
|
+
// 如果没有,添加 importModule 导入
|
|
36
|
+
if (!hasImportModule) {
|
|
37
|
+
const hooksPath = pkgJson.name;
|
|
38
|
+
result += `import { importModule } from '${hooksPath}';\n`;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// 转换相对路径的 import
|
|
42
|
+
// 注意:先转换 source,然后再拼接
|
|
43
|
+
const transformedSource = source.replace(
|
|
44
|
+
/^import\s+(['"])(\.[^'"]+)\1;?\s*$/gm,
|
|
45
|
+
(match, quote, importPath) => {
|
|
46
|
+
return `await importModule(${quote}${importPath}${quote});`;
|
|
47
|
+
}
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
result += transformedSource;
|
|
51
|
+
|
|
52
|
+
return result;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* 检查文件是否需要处理
|
|
58
|
+
*
|
|
59
|
+
* 环境变量配置:
|
|
60
|
+
* - DEPENDENCY_TREE_INCLUDE: 需要处理的路径(优先级最高,即使在 node_modules 中也会处理)
|
|
61
|
+
* - DEPENDENCY_TREE_EXCLUDE: 需要排除的路径(可用于排除特定的 node_modules 包)
|
|
62
|
+
*
|
|
63
|
+
* @example
|
|
64
|
+
* // 包含 npm 包中的插件
|
|
65
|
+
* DEPENDENCY_TREE_INCLUDE=node_modules/@my-org/my-plugin/plugins
|
|
66
|
+
*
|
|
67
|
+
* // 包含多个路径
|
|
68
|
+
* DEPENDENCY_TREE_INCLUDE=src/plugins,node_modules/@my-org/plugin1,node_modules/@my-org/plugin2
|
|
69
|
+
*
|
|
70
|
+
* // 排除特定路径(与默认的 node_modules 排除结合使用)
|
|
71
|
+
* DEPENDENCY_TREE_EXCLUDE=node_modules/some-lib
|
|
72
|
+
*/
|
|
73
|
+
export function shouldTransformPath(path, extensions = ['.ts', '.js']) {
|
|
74
|
+
// 去除查询参数(如 ?t=timestamp)
|
|
75
|
+
const [actualPath] = path.split('?');
|
|
76
|
+
|
|
77
|
+
// 检查扩展名
|
|
78
|
+
const hasValidExtension = extensions.some(ext => actualPath.endsWith(ext));
|
|
79
|
+
if (!hasValidExtension) {
|
|
80
|
+
return false;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// 1. 检查 INCLUDE 路径(优先级最高,即使在 node_modules 中也处理)
|
|
84
|
+
const includePaths = process.env.DEPENDENCY_TREE_INCLUDE;
|
|
85
|
+
if (includePaths) {
|
|
86
|
+
const paths = includePaths.split(',').map(p => p.trim());
|
|
87
|
+
if (paths.some(p => actualPath.includes(p))) {
|
|
88
|
+
return true; // 明确包含,直接返回 true
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// 2. 检查 EXCLUDE 路径(优先级第二)
|
|
93
|
+
const excludePaths = process.env.DEPENDENCY_TREE_EXCLUDE;
|
|
94
|
+
if (excludePaths) {
|
|
95
|
+
const paths = excludePaths.split(',').map(p => p.trim());
|
|
96
|
+
if (paths.some(p => actualPath.includes(p))) {
|
|
97
|
+
return false; // 明确排除
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// 3. 默认规则:排除所有 node_modules,除非明确 INCLUDE
|
|
102
|
+
if (actualPath.includes('/node_modules/')) {
|
|
103
|
+
return false;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
return true;
|
|
107
|
+
}
|
|
108
|
+
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* tsx Loader Hook
|
|
3
|
+
* 在 tsx 运行时拦截模块加载,自动转换 import 语句
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { readFileSync } from 'fs';
|
|
7
|
+
import { fileURLToPath } from 'url';
|
|
8
|
+
import { transformImports, hasRelativeImports, shouldTransformPath } from './transform-utils.mjs';
|
|
9
|
+
|
|
10
|
+
// 当前正在加载的依赖栈
|
|
11
|
+
const dependencyStack = [];
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* resolve hook
|
|
15
|
+
*/
|
|
16
|
+
export async function resolve(specifier, context, nextResolve) {
|
|
17
|
+
return nextResolve(specifier, context);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* 检查文件是否需要处理
|
|
22
|
+
*/
|
|
23
|
+
function shouldTransform(url) {
|
|
24
|
+
return shouldTransformPath(url, ['.ts', '.js']);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* load hook - 拦截模块加载并转换代码
|
|
29
|
+
*/
|
|
30
|
+
export async function load(url, context, nextLoad) {
|
|
31
|
+
// 检查是否需要处理此文件
|
|
32
|
+
if (shouldTransform(url)) {
|
|
33
|
+
try {
|
|
34
|
+
// 检查是否是热重载(URL 包含时间戳查询参数)
|
|
35
|
+
const isHotReload = url.includes('?t=');
|
|
36
|
+
|
|
37
|
+
// 从 URL 中分离实际路径
|
|
38
|
+
const [actualUrl] = url.split('?');
|
|
39
|
+
const filePath = fileURLToPath(actualUrl);
|
|
40
|
+
let source = readFileSync(filePath, 'utf-8');
|
|
41
|
+
|
|
42
|
+
// 检查是否已经转换过(热重载时强制重新转换)
|
|
43
|
+
if (!isHotReload && source.includes('__LOADER_TRANSFORMED__')) {
|
|
44
|
+
return nextLoad(url, context);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// 检查是否有相对路径的 import 需要转换
|
|
48
|
+
if (!isHotReload && !hasRelativeImports(source)) {
|
|
49
|
+
return nextLoad(url, context);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// 转换相对路径的 import 语句
|
|
53
|
+
const transformed = transformImports(source, actualUrl, isHotReload, '__LOADER_TRANSFORMED__');
|
|
54
|
+
|
|
55
|
+
return {
|
|
56
|
+
format: 'module',
|
|
57
|
+
source: transformed,
|
|
58
|
+
shortCircuit: true,
|
|
59
|
+
};
|
|
60
|
+
} catch (error) {
|
|
61
|
+
console.error('Loader error:', error);
|
|
62
|
+
return nextLoad(url, context);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// 其他文件使用默认加载
|
|
67
|
+
return nextLoad(url, context);
|
|
68
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@zhin.js/dependency",
|
|
3
|
+
"version": "1.0.1",
|
|
4
|
+
"description": "A TypeScript dependency tree analyzer with ESM support",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.js"
|
|
12
|
+
},
|
|
13
|
+
"./register.mjs": "./loaders/register.mjs",
|
|
14
|
+
"./tsx-loader.mjs": "./loaders/tsx-loader.mjs",
|
|
15
|
+
"./bun-plugin": "./loaders/bun-plugin.ts",
|
|
16
|
+
"./bun-preload.ts": "./loaders/bun-preload.ts"
|
|
17
|
+
},
|
|
18
|
+
"files": [
|
|
19
|
+
"dist",
|
|
20
|
+
"loaders",
|
|
21
|
+
"README.md"
|
|
22
|
+
],
|
|
23
|
+
"keywords": [
|
|
24
|
+
"dependency",
|
|
25
|
+
"tree",
|
|
26
|
+
"module",
|
|
27
|
+
"import",
|
|
28
|
+
"loader",
|
|
29
|
+
"typescript",
|
|
30
|
+
"esm"
|
|
31
|
+
],
|
|
32
|
+
"author": "",
|
|
33
|
+
"license": "ISC",
|
|
34
|
+
"devDependencies": {
|
|
35
|
+
"@types/node": "^20.10.0",
|
|
36
|
+
"@types/bun": "^1.2.0",
|
|
37
|
+
"typescript": "^5.3.0"
|
|
38
|
+
},
|
|
39
|
+
"scripts": {
|
|
40
|
+
"build": "tsc",
|
|
41
|
+
"dev": "tsc --watch",
|
|
42
|
+
"clean": "rm -rf dist",
|
|
43
|
+
"type-check": "tsc --noEmit"
|
|
44
|
+
}
|
|
45
|
+
}
|