feat: manually mirror opencoze's code from bytedance

Change-Id: I09a73aadda978ad9511264a756b2ce51f5761adf
This commit is contained in:
fanlv
2025-07-20 17:36:12 +08:00
commit 890153324f
14811 changed files with 1923430 additions and 0 deletions

View File

@@ -0,0 +1,118 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import path, { relative } from 'path';
import { Rule } from 'eslint';
import readPkgUp from 'eslint-module-utils/readPkgUp';
import resolve from 'eslint-module-utils/resolve';
import { exportPathMatch } from './utils';
export const noPkgDirImport: Rule.RuleModule = {
meta: {
type: 'problem',
docs: {
description: 'limit import package directory directly',
},
messages: {
invalidSubpath:
'subPath `{{ subPath }}` is NOT exported in `{{ pkg }}`, you can config the `exports` fields in package.json',
noExportsCfg:
"NO `exports` fields config in `{{ pkg }}` package.json, you can't import by subPath ",
},
},
create(context) {
return {
ImportDeclaration(node) {
const importPath = `${node.source.value}`;
const modulePath = resolve(importPath, context);
if (!modulePath) {
// 解析不到的情况,暂不处理
return;
}
const { pkg, path: importPkgPath } = readPkgUp({
cwd: modulePath,
}) as any;
const { path: currentPkgPath } = readPkgUp({
cwd: context.filename,
}) as any;
if (!pkg.name) {
return;
}
// 本地link会解析到node_modules目录需要拿到pkg name再次解析。
const moduleRealPath = resolve(pkg.name, context);
if (
// 包名称就是引用路径
pkg.name === importPath ||
// 解析到其他包,如@type
!importPath.startsWith(pkg.name) ||
// 解析到自己包的文件
currentPkgPath === importPkgPath ||
!moduleRealPath ||
moduleRealPath.includes('node_modules')
) {
return;
}
if (!pkg.exports) {
context.report({
messageId: 'noExportsCfg',
data: {
pkg: pkg.name,
},
// @ts-expect-error -- linter-disable-autofix
loc: node.loc,
});
} else if (pkg.exports) {
if (typeof pkg.exports === 'string') {
context.report({
messageId: 'noExportsCfg',
data: {
pkg: pkg.name,
},
// @ts-expect-error -- linter-disable-autofix
loc: node.loc,
});
return;
}
const validSubPath = Object.keys(pkg.exports);
if (
!validSubPath.some(p => {
const pkgExportPath = path.join(pkg.name, p);
return exportPathMatch(importPath, pkgExportPath);
})
) {
const subPath = relative(pkg.name, importPath);
context.report({
messageId: 'invalidSubpath',
data: {
subPath,
pkg: pkg.name,
},
// @ts-expect-error -- linter-disable-autofix
loc: node.loc,
});
}
}
},
};
},
};

View File

@@ -0,0 +1,247 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { RuleTester } from 'eslint';
import resolve from 'eslint-module-utils/resolve';
import readPkgUp from 'eslint-module-utils/readPkgUp';
import { noPkgDirImport } from '../index';
const ruleTester = new RuleTester({});
vi.mock('eslint-module-utils/resolve', () => ({
default: vi.fn(),
}));
vi.mock('eslint-module-utils/readPkgUp', () => ({
default: vi.fn(),
}));
const validCases = [
{
code: 'import "xxx"',
modulePath: undefined, // modulePath 为 空
moduleRealPath: undefined,
importPkgPath: 'path/to/import/pkg',
currentPkgPath: 'path/to/current/pkg',
pkg: {
name: '',
exports: {},
},
},
{
code: "import pkg from 'some/pkg';",
modulePath: 'path/to/module',
moduleRealPath: 'path/to/module',
importPkgPath: 'path/to/import/pkg',
currentPkgPath: 'path/to/current/pkg',
pkg: {
name: 'some/pkg', // 包名称与引用路径相同
exports: {},
},
},
{
code: "import pkg from 'some/pkg';",
modulePath: 'path/to/module',
moduleRealPath: 'path/to/module',
importPkgPath: 'path/to/import/pkg',
currentPkgPath: 'path/to/current/pkg',
pkg: {
name: undefined, // 解析到不规范配置的package.json
},
},
{
code: "import pkg from 'pkg';",
modulePath: 'path/to/module',
moduleRealPath: 'path/to/module',
importPkgPath: 'path/to/import/pkg',
currentPkgPath: 'path/to/current/pkg',
pkg: {
name: '@types/pkg', // 解析到类型包
exports: {},
},
},
{
code: "import pkg from 'pkg';",
modulePath: 'path/to/module',
moduleRealPath: 'path/to/module',
importPkgPath: 'path/to/same/pkg', // 相同路径
currentPkgPath: 'path/to/same/pkg',
pkg: {
name: '@types/pkg',
exports: {},
},
},
{
code: "import pkg from 'pkg';",
modulePath: 'path/to/module',
moduleRealPath: undefined,
importPkgPath: 'path/to/import/pkg',
currentPkgPath: 'path/to/current/pkg',
pkg: {
name: 'pkg',
exports: {},
},
},
{
code: "import pkg from 'pkg';",
modulePath: 'path/to/module',
moduleRealPath: 'path/to/node_modules/pkg', // 解析到node_modules
importPkgPath: 'path/to/import/pkg',
currentPkgPath: 'path/to/current/pkg',
pkg: {
name: 'pkg',
exports: {},
},
},
{
code: "import pkg from 'pkg/subPath';",
modulePath: 'path/to/pkg',
moduleRealPath: 'path/to/pkg',
importPkgPath: 'path/to/import/pkg',
currentPkgPath: 'path/to/current/pkg',
pkg: {
name: 'pkg',
exports: { subPath: './subPath' },
},
},
{
code: "import pkg from 'pkg/sub/path';",
modulePath: 'path/to/pkg',
moduleRealPath: 'path/to/pkg',
importPkgPath: 'path/to/import/pkg',
currentPkgPath: 'path/to/current/pkg',
pkg: {
name: 'pkg',
exports: { 'sub/*': './subPath' },
},
},
].map(c => {
vi.mocked(resolve).mockReturnValueOnce(c.modulePath);
if (!c.modulePath) {
return {
code: c.code,
// TODO: 避免eslint duplication检测。可能需要改为其他方式
settings: c,
};
}
if (c.pkg.name) {
vi.mocked(resolve).mockReturnValueOnce(c.moduleRealPath);
}
vi.mocked(readPkgUp)
.mockReturnValueOnce({
pkg: c.pkg,
path: c.importPkgPath,
})
.mockReturnValueOnce({
path: c.currentPkgPath,
});
return {
code: c.code,
settings: c,
};
});
const invalidCases = [
{
code: "import pkg from 'pkg/subPath';",
modulePath: 'path/to/pkg',
moduleRealPath: 'path/to/pkg',
importPkgPath: 'path/to/import/pkg',
currentPkgPath: 'path/to/current/pkg',
pkg: {
name: 'pkg',
exports: undefined, // 为空
},
messageId: 'noExportsCfg',
},
{
code: "import pkg from 'pkg/subPath';",
modulePath: 'path/to/pkg',
moduleRealPath: 'path/to/pkg',
importPkgPath: 'path/to/import/pkg',
currentPkgPath: 'path/to/current/pkg',
pkg: {
name: 'pkg',
exports: 'main.js', // isString
},
messageId: 'noExportsCfg',
},
{
code: "import pkg from 'pkg/subPath';",
modulePath: 'path/to/pkg',
moduleRealPath: 'path/to/pkg',
importPkgPath: 'path/to/import/pkg',
currentPkgPath: 'path/to/current/pkg',
pkg: {
name: 'pkg',
exports: { otherPath: 'otherPath' },
},
messageId: 'invalidSubpath',
},
{
code: "import pkg from 'pkg/sub/path';",
modulePath: 'path/to/pkg',
moduleRealPath: 'path/to/pkg',
importPkgPath: 'path/to/import/pkg',
currentPkgPath: 'path/to/current/pkg',
pkg: {
name: 'pkg',
exports: {
sub: './sub',
},
},
messageId: 'invalidSubpath',
},
].map(c => {
vi.mocked(resolve).mockReturnValueOnce(c.modulePath);
if (!c.modulePath) {
return {
settings: c,
code: c.code,
errors: [],
};
}
vi.mocked(resolve).mockReturnValueOnce(c.moduleRealPath);
vi.mocked(readPkgUp)
.mockReturnValueOnce({
pkg: c.pkg,
path: c.importPkgPath,
})
.mockReturnValueOnce({
path: c.currentPkgPath,
});
return {
settings: c,
code: c.code,
errors: [
{
messageId: c.messageId,
},
],
};
});
ruleTester.run('no-pkg-dir-import', noPkgDirImport, {
valid: [...validCases],
invalid: [...invalidCases],
});

View File

@@ -0,0 +1,45 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { exportPathMatch } from '../utils';
describe('exportPathMatch', () => {
it.each([
['./foo', './foo'],
['./foo.js', './*'],
['./foo.js', './*.js'],
['./foo/baz', './foo/*'],
['./foo/baz/baz.js', './foo/*'],
])(
'import path is %s, export path is %s, should be matched',
(importPath, exportPath) => {
expect(exportPathMatch(importPath, exportPath)).toBe(true);
},
);
it.each([
['./foo', './bar'],
['./foo.js', './*.ts'],
['./foo.js', './foo.ts'],
['./baz/bar', './foo/*'],
['./foo/bar/baz.js', './foo/*.js'],
])(
'import path is %s, export path is %s, should NOT be matched',
(importPath, exportPath) => {
expect(exportPathMatch(importPath, exportPath)).toBe(false);
},
);
});

View File

@@ -0,0 +1,34 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import path from 'path';
export function exportPathMatch(importPath: string, pkgExportPath: string) {
if (importPath === pkgExportPath) {
return true;
}
const pkgExportBasename = path.basename(pkgExportPath);
if (importPath.startsWith(path.dirname(pkgExportPath))) {
if (pkgExportBasename === '*') {
return true;
}
if (path.dirname(importPath) === path.dirname(pkgExportPath)) {
return pkgExportBasename === `*${path.extname(importPath)}`;
}
}
return false;
}