@vitus-labs/tools-core 1.8.1-alpha.0 → 1.8.1-alpha.2
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/lib/index.js +105 -33
- package/lib/index.js.map +1 -1
- package/lib/types/index.d.ts +2 -1
- package/lib/types/index.d.ts.map +1 -1
- package/package.json +3 -8
- package/src/index.test.ts +187 -77
- package/src/index.ts +122 -35
package/lib/index.js
CHANGED
|
@@ -1,40 +1,92 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import { createRequire } from 'node:module';
|
|
3
|
-
import
|
|
4
|
-
|
|
5
|
-
const VITUS_LABS_FILE_NAME = 'vl-tools.config.js';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
const VL_CONFIG_FILES = ['vl-tools.config.mjs', 'vl-tools.config.js'];
|
|
6
5
|
const PACKAGE_FILE_NAME = 'package.json';
|
|
7
6
|
const TYPESCRIPT_FILE_NAME = 'tsconfig.json';
|
|
8
7
|
const require = createRequire(import.meta.url);
|
|
9
8
|
// --------------------------------------------------------
|
|
9
|
+
// Utility helpers (replaces lodash-es and find-up)
|
|
10
|
+
// --------------------------------------------------------
|
|
11
|
+
const get = (obj, dotPath, defaultValue = {}) => {
|
|
12
|
+
const keys = dotPath.split('.');
|
|
13
|
+
let result = obj;
|
|
14
|
+
for (const key of keys) {
|
|
15
|
+
if (result == null)
|
|
16
|
+
return defaultValue;
|
|
17
|
+
result = result[key];
|
|
18
|
+
}
|
|
19
|
+
return result === undefined ? defaultValue : result;
|
|
20
|
+
};
|
|
21
|
+
const deepMerge = (target, source) => {
|
|
22
|
+
const result = { ...target };
|
|
23
|
+
for (const key of Object.keys(source)) {
|
|
24
|
+
const srcVal = source[key];
|
|
25
|
+
const tgtVal = result[key];
|
|
26
|
+
if (typeof srcVal === 'object' &&
|
|
27
|
+
srcVal !== null &&
|
|
28
|
+
!Array.isArray(srcVal) &&
|
|
29
|
+
typeof tgtVal === 'object' &&
|
|
30
|
+
tgtVal !== null &&
|
|
31
|
+
!Array.isArray(tgtVal)) {
|
|
32
|
+
result[key] = deepMerge(tgtVal, srcVal);
|
|
33
|
+
}
|
|
34
|
+
else {
|
|
35
|
+
result[key] = srcVal;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return result;
|
|
39
|
+
};
|
|
40
|
+
const findFileUp = (names, cwd = process.cwd()) => {
|
|
41
|
+
const fileNames = Array.isArray(names) ? names : [names];
|
|
42
|
+
let dir = path.resolve(cwd);
|
|
43
|
+
while (true) {
|
|
44
|
+
for (const name of fileNames) {
|
|
45
|
+
const filePath = path.join(dir, name);
|
|
46
|
+
try {
|
|
47
|
+
if (fs.statSync(filePath).isFile())
|
|
48
|
+
return filePath;
|
|
49
|
+
}
|
|
50
|
+
catch (_e) {
|
|
51
|
+
// file doesn't exist, continue
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
const parent = path.dirname(dir);
|
|
55
|
+
if (parent === dir)
|
|
56
|
+
return undefined;
|
|
57
|
+
dir = parent;
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
// --------------------------------------------------------
|
|
10
61
|
// FIND & READ file helpers
|
|
11
62
|
// --------------------------------------------------------
|
|
12
|
-
const findFile = (filename) =>
|
|
63
|
+
const findFile = (filename) => findFileUp(filename);
|
|
64
|
+
const loadModule = (filePath) => {
|
|
65
|
+
try {
|
|
66
|
+
const imported = require(filePath);
|
|
67
|
+
// Handle ESM default export wrapping
|
|
68
|
+
return imported?.default ?? imported ?? {};
|
|
69
|
+
}
|
|
70
|
+
catch (_e) {
|
|
71
|
+
return {};
|
|
72
|
+
}
|
|
73
|
+
};
|
|
13
74
|
const loadFileToJSON = (filename) => {
|
|
14
75
|
const file = findFile(filename);
|
|
15
76
|
if (!file)
|
|
16
77
|
return {};
|
|
17
|
-
let data = {};
|
|
18
78
|
// try to read an exported module first
|
|
79
|
+
const data = loadModule(file);
|
|
80
|
+
if (data && Object.keys(data).length > 0)
|
|
81
|
+
return data;
|
|
82
|
+
// try to read a plain json file like tsconfig.json
|
|
19
83
|
try {
|
|
20
|
-
|
|
21
|
-
if (importedFile) {
|
|
22
|
-
data = importedFile;
|
|
23
|
-
}
|
|
84
|
+
return JSON.parse(fs.readFileSync(file, 'utf-8'));
|
|
24
85
|
}
|
|
25
86
|
catch (_e) {
|
|
26
87
|
// ignore error
|
|
27
88
|
}
|
|
28
|
-
|
|
29
|
-
if (!data) {
|
|
30
|
-
try {
|
|
31
|
-
data = JSON.parse(fs.readFileSync(file, 'utf-8'));
|
|
32
|
-
}
|
|
33
|
-
catch (_e) {
|
|
34
|
-
// ignore error
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
return data;
|
|
89
|
+
return {};
|
|
38
90
|
};
|
|
39
91
|
// --------------------------------------------------------
|
|
40
92
|
// GET PACKAGE.JSON info
|
|
@@ -56,9 +108,6 @@ const getDependenciesList = (types) => {
|
|
|
56
108
|
});
|
|
57
109
|
return result;
|
|
58
110
|
};
|
|
59
|
-
// parse namespace name
|
|
60
|
-
// const parseNamespace = (name) =>
|
|
61
|
-
// name.startsWith('@') ? name.split('/')[0] : ''
|
|
62
111
|
// converts package name to umd or iife valid format
|
|
63
112
|
// example: napespace-package-name => namespacePackageName
|
|
64
113
|
const camelspaceBundleName = (name) => {
|
|
@@ -76,13 +125,8 @@ const camelspaceBundleName = (name) => {
|
|
|
76
125
|
const getPkgData = () => {
|
|
77
126
|
const pkg = getPackageJSON();
|
|
78
127
|
const { name } = pkg;
|
|
79
|
-
// const namespace = parseNamespace(name)
|
|
80
128
|
return {
|
|
81
129
|
...pkg,
|
|
82
|
-
// nameWithoutPrefix: name.replace(namespace, '').replace('/', ''),
|
|
83
|
-
// namespace,
|
|
84
|
-
// namespaceName: namespace.replace('@', ''),
|
|
85
|
-
// rootPath: findFilePath('package.json'),
|
|
86
130
|
bundleName: camelspaceBundleName(name),
|
|
87
131
|
externalDependencies: getDependenciesList([
|
|
88
132
|
'dependencies',
|
|
@@ -92,11 +136,38 @@ const getPkgData = () => {
|
|
|
92
136
|
};
|
|
93
137
|
// --------------------------------------------------------
|
|
94
138
|
// LOAD EXTERNAL CONFIGURATION
|
|
139
|
+
// Cascading: finds all vl-tools.config.{mjs,js} files from
|
|
140
|
+
// cwd upward, then deep-merges them (root first, closest
|
|
141
|
+
// package config wins).
|
|
95
142
|
// --------------------------------------------------------
|
|
96
|
-
const
|
|
143
|
+
const findAllConfigFiles = () => {
|
|
144
|
+
const files = [];
|
|
145
|
+
let cwd = process.cwd();
|
|
146
|
+
while (true) {
|
|
147
|
+
const file = findFileUp(VL_CONFIG_FILES, cwd);
|
|
148
|
+
if (!file)
|
|
149
|
+
break;
|
|
150
|
+
files.push(file);
|
|
151
|
+
const parentDir = path.dirname(path.dirname(file));
|
|
152
|
+
if (parentDir === path.dirname(file))
|
|
153
|
+
break;
|
|
154
|
+
cwd = parentDir;
|
|
155
|
+
}
|
|
156
|
+
// Root config first, closest config last (overrides)
|
|
157
|
+
return files.reverse();
|
|
158
|
+
};
|
|
159
|
+
const getExternalConfig = () => {
|
|
160
|
+
const files = findAllConfigFiles();
|
|
161
|
+
let config = {};
|
|
162
|
+
for (const file of files) {
|
|
163
|
+
const loaded = loadModule(file);
|
|
164
|
+
config = deepMerge(config, loaded);
|
|
165
|
+
}
|
|
166
|
+
return config;
|
|
167
|
+
};
|
|
97
168
|
const loadConfigParam = (filename) => (key, defaultValue = {}) => {
|
|
98
169
|
const externalConfig = loadFileToJSON(filename);
|
|
99
|
-
return
|
|
170
|
+
return get(externalConfig, key, defaultValue);
|
|
100
171
|
};
|
|
101
172
|
const loadVLToolsConfig = () => {
|
|
102
173
|
const externalConfig = getExternalConfig();
|
|
@@ -104,15 +175,16 @@ const loadVLToolsConfig = () => {
|
|
|
104
175
|
get config() {
|
|
105
176
|
return object;
|
|
106
177
|
},
|
|
107
|
-
get: (param, defaultValue) =>
|
|
108
|
-
merge: (param) => cloneAndEnhance(
|
|
178
|
+
get: (param, defaultValue) => get(object, param, defaultValue || {}),
|
|
179
|
+
merge: (param) => cloneAndEnhance(deepMerge(param, object)),
|
|
109
180
|
});
|
|
110
181
|
const getOutput = (key) => {
|
|
111
|
-
const result =
|
|
182
|
+
const result = get(externalConfig, key, {});
|
|
112
183
|
return cloneAndEnhance(result);
|
|
113
184
|
};
|
|
114
185
|
return getOutput;
|
|
115
186
|
};
|
|
187
|
+
const defineConfig = (config) => config;
|
|
116
188
|
const swapGlobals = (globals) => Object.entries(globals).reduce((acc, [key, value]) => {
|
|
117
189
|
acc[value] = key;
|
|
118
190
|
return acc;
|
|
@@ -120,5 +192,5 @@ const swapGlobals = (globals) => Object.entries(globals).reduce((acc, [key, valu
|
|
|
120
192
|
const PKG = getPkgData();
|
|
121
193
|
const VL_CONFIG = loadVLToolsConfig();
|
|
122
194
|
const TS_CONFIG = loadFileToJSON(TYPESCRIPT_FILE_NAME);
|
|
123
|
-
export { findFile, loadConfigParam, loadFileToJSON, loadVLToolsConfig, swapGlobals, PKG, VL_CONFIG, TS_CONFIG, };
|
|
195
|
+
export { defineConfig, findFile, loadConfigParam, loadFileToJSON, loadVLToolsConfig, swapGlobals, PKG, VL_CONFIG, TS_CONFIG, };
|
|
124
196
|
//# sourceMappingURL=index.js.map
|
package/lib/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAA;AACxB,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAC3C,OAAO,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/lib/types/index.d.ts
CHANGED
|
@@ -6,6 +6,7 @@ declare const loadVLToolsConfig: () => (key: string) => {
|
|
|
6
6
|
get: (param: string, defaultValue?: any) => any;
|
|
7
7
|
merge: (param: Record<string, any>) => /*elided*/ any;
|
|
8
8
|
};
|
|
9
|
+
declare const defineConfig: <T extends Record<string, any>>(config: T) => T;
|
|
9
10
|
declare const swapGlobals: (globals: Record<string, string>) => Record<string, string>;
|
|
10
11
|
declare const PKG: Record<string, any>;
|
|
11
12
|
declare const VL_CONFIG: (key: string) => {
|
|
@@ -14,5 +15,5 @@ declare const VL_CONFIG: (key: string) => {
|
|
|
14
15
|
merge: (param: Record<string, any>) => /*elided*/ any;
|
|
15
16
|
};
|
|
16
17
|
declare const TS_CONFIG: Record<string, any>;
|
|
17
|
-
export { findFile, loadConfigParam, loadFileToJSON, loadVLToolsConfig, swapGlobals, PKG, VL_CONFIG, TS_CONFIG, };
|
|
18
|
+
export { defineConfig, findFile, loadConfigParam, loadFileToJSON, loadVLToolsConfig, swapGlobals, PKG, VL_CONFIG, TS_CONFIG, };
|
|
18
19
|
//# sourceMappingURL=index.d.ts.map
|
package/lib/types/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AA8EA,QAAA,MAAM,QAAQ,GAAI,UAAU,MAAM,uBAAyB,CAAA;AAY3D,QAAA,MAAM,cAAc,GAAI,UAAU,MAAM,KAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAiB5D,CAAA;AAgGD,QAAA,MAAM,eAAe,GAClB,UAAU,MAAM,MAChB,KAAK,MAAM,EAAE,iBAAiB,QAI9B,CAAA;AAEH,QAAA,MAAM,iBAAiB,cAaG,MAAM;;iBANf,MAAM,iBAAiB,GAAG;mBAExB,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC;CAWrC,CAAA;AAED,QAAA,MAAM,YAAY,GAAI,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,QAAQ,CAAC,KAAG,CAAW,CAAA;AAE5E,QAAA,MAAM,WAAW,GAAI,SAAS,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,2BAOjD,CAAA;AAEH,QAAA,MAAM,GAAG,qBAAe,CAAA;AACxB,QAAA,MAAM,SAAS,QArBW,MAAM;;iBANf,MAAM,iBAAiB,GAAG;mBAExB,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC;CAyBD,CAAA;AACrC,QAAA,MAAM,SAAS,qBAAuC,CAAA;AAEtD,OAAO,EACL,YAAY,EACZ,QAAQ,EACR,eAAe,EACf,cAAc,EACd,iBAAiB,EACjB,WAAW,EACX,GAAG,EACH,SAAS,EACT,SAAS,GACV,CAAA"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vitus-labs/tools-core",
|
|
3
|
-
"version": "1.8.1-alpha.
|
|
3
|
+
"version": "1.8.1-alpha.2+6f381e3",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -29,14 +29,9 @@
|
|
|
29
29
|
"build": "tsc",
|
|
30
30
|
"typecheck": "tsc --noEmit"
|
|
31
31
|
},
|
|
32
|
-
"dependencies": {
|
|
33
|
-
"find-up": "^8.0.0",
|
|
34
|
-
"lodash-es": "^4.17.23"
|
|
35
|
-
},
|
|
36
32
|
"devDependencies": {
|
|
37
|
-
"@
|
|
38
|
-
"@vitus-labs/tools-typescript": "1.8.1-alpha.0+120486e",
|
|
33
|
+
"@vitus-labs/tools-typescript": "1.8.1-alpha.2+6f381e3",
|
|
39
34
|
"typescript": "^5.9.3"
|
|
40
35
|
},
|
|
41
|
-
"gitHead": "
|
|
36
|
+
"gitHead": "6f381e35e5829bc1fae660817f19bfa5098674d6"
|
|
42
37
|
}
|
package/src/index.test.ts
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
|
2
2
|
|
|
3
|
-
const
|
|
4
|
-
vi.mock('find-up', () => ({
|
|
5
|
-
findUpSync: mockFindUpSync,
|
|
6
|
-
}))
|
|
7
|
-
|
|
3
|
+
const mockStatSync = vi.fn()
|
|
8
4
|
const mockReadFileSync = vi.fn()
|
|
9
5
|
vi.mock('node:fs', () => ({
|
|
10
|
-
default: {
|
|
6
|
+
default: {
|
|
7
|
+
statSync: mockStatSync,
|
|
8
|
+
readFileSync: mockReadFileSync,
|
|
9
|
+
},
|
|
10
|
+
statSync: mockStatSync,
|
|
11
11
|
readFileSync: mockReadFileSync,
|
|
12
12
|
}))
|
|
13
13
|
|
|
@@ -16,25 +16,32 @@ vi.mock('node:module', () => ({
|
|
|
16
16
|
createRequire: vi.fn(() => mockRequireFn),
|
|
17
17
|
}))
|
|
18
18
|
|
|
19
|
+
// Helper: make specific paths "exist" for findFileUp
|
|
20
|
+
const makeFilesExist = (paths: string[]) => {
|
|
21
|
+
mockStatSync.mockImplementation((filePath: string) => {
|
|
22
|
+
if (paths.includes(filePath)) return { isFile: () => true }
|
|
23
|
+
throw new Error('ENOENT')
|
|
24
|
+
})
|
|
25
|
+
}
|
|
26
|
+
|
|
19
27
|
// Default mock setup that satisfies module-level getPkgData() —
|
|
20
28
|
// package.json must always be found with a valid `name` field,
|
|
21
29
|
// otherwise camelspaceBundleName(undefined) crashes at import time.
|
|
22
30
|
const setupDefaultMocks = () => {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
mockRequireFn.mockImplementation((path: string) => {
|
|
28
|
-
if (path === '/mock/package.json') return { name: 'mock-pkg' }
|
|
31
|
+
vi.spyOn(process, 'cwd').mockReturnValue('/mock/project')
|
|
32
|
+
makeFilesExist(['/mock/project/package.json'])
|
|
33
|
+
mockRequireFn.mockImplementation((p: string) => {
|
|
34
|
+
if (p === '/mock/project/package.json') return { name: 'mock-pkg' }
|
|
29
35
|
return null
|
|
30
36
|
})
|
|
31
37
|
}
|
|
32
38
|
|
|
33
39
|
describe('tools-core', () => {
|
|
34
40
|
beforeEach(() => {
|
|
35
|
-
|
|
41
|
+
mockStatSync.mockReset()
|
|
36
42
|
mockReadFileSync.mockReset()
|
|
37
43
|
mockRequireFn.mockReset()
|
|
44
|
+
vi.restoreAllMocks()
|
|
38
45
|
})
|
|
39
46
|
|
|
40
47
|
describe('swapGlobals', () => {
|
|
@@ -58,6 +65,22 @@ describe('tools-core', () => {
|
|
|
58
65
|
})
|
|
59
66
|
})
|
|
60
67
|
|
|
68
|
+
describe('defineConfig', () => {
|
|
69
|
+
let defineConfig: <T extends Record<string, any>>(config: T) => T
|
|
70
|
+
|
|
71
|
+
beforeEach(async () => {
|
|
72
|
+
vi.resetModules()
|
|
73
|
+
setupDefaultMocks()
|
|
74
|
+
const mod = await import('./index.js')
|
|
75
|
+
defineConfig = mod.defineConfig
|
|
76
|
+
})
|
|
77
|
+
|
|
78
|
+
it('should return the same config object', () => {
|
|
79
|
+
const config = { stories: { framework: 'next' } }
|
|
80
|
+
expect(defineConfig(config)).toBe(config)
|
|
81
|
+
})
|
|
82
|
+
})
|
|
83
|
+
|
|
61
84
|
describe('findFile', () => {
|
|
62
85
|
let findFile: (filename: string) => string | undefined
|
|
63
86
|
|
|
@@ -68,19 +91,17 @@ describe('tools-core', () => {
|
|
|
68
91
|
findFile = mod.findFile
|
|
69
92
|
})
|
|
70
93
|
|
|
71
|
-
it('should
|
|
72
|
-
|
|
73
|
-
findFile('
|
|
74
|
-
expect(mockFindUpSync).toHaveBeenCalledWith('file.json', { type: 'file' })
|
|
94
|
+
it('should return the path when file is found in cwd', () => {
|
|
95
|
+
makeFilesExist(['/mock/project/package.json', '/mock/project/some.json'])
|
|
96
|
+
expect(findFile('some.json')).toBe('/mock/project/some.json')
|
|
75
97
|
})
|
|
76
98
|
|
|
77
|
-
it('should
|
|
78
|
-
|
|
79
|
-
expect(findFile('
|
|
99
|
+
it('should walk up directories to find the file', () => {
|
|
100
|
+
makeFilesExist(['/mock/project/package.json', '/mock/some.json'])
|
|
101
|
+
expect(findFile('some.json')).toBe('/mock/some.json')
|
|
80
102
|
})
|
|
81
103
|
|
|
82
104
|
it('should return undefined when file is not found', () => {
|
|
83
|
-
mockFindUpSync.mockReturnValue(undefined)
|
|
84
105
|
expect(findFile('missing.json')).toBeUndefined()
|
|
85
106
|
})
|
|
86
107
|
})
|
|
@@ -96,27 +117,46 @@ describe('tools-core', () => {
|
|
|
96
117
|
})
|
|
97
118
|
|
|
98
119
|
it('should return empty object when file is not found', () => {
|
|
99
|
-
mockFindUpSync.mockReturnValue(undefined)
|
|
100
120
|
expect(loadFileToJSON('missing.json')).toEqual({})
|
|
101
121
|
})
|
|
102
122
|
|
|
103
123
|
it('should load file using require when available', () => {
|
|
104
|
-
|
|
105
|
-
mockRequireFn.
|
|
124
|
+
makeFilesExist(['/mock/project/package.json', '/mock/project/config.js'])
|
|
125
|
+
mockRequireFn.mockImplementation((p: string) => {
|
|
126
|
+
if (p === '/mock/project/config.js') return { key: 'value' }
|
|
127
|
+
return null
|
|
128
|
+
})
|
|
106
129
|
expect(loadFileToJSON('config.js')).toEqual({ key: 'value' })
|
|
107
130
|
})
|
|
108
131
|
|
|
109
|
-
it('should
|
|
110
|
-
|
|
111
|
-
mockRequireFn.
|
|
112
|
-
|
|
132
|
+
it('should unwrap ESM default export', () => {
|
|
133
|
+
makeFilesExist(['/mock/project/package.json', '/mock/project/config.mjs'])
|
|
134
|
+
mockRequireFn.mockImplementation((p: string) => {
|
|
135
|
+
if (p === '/mock/project/config.mjs')
|
|
136
|
+
return { default: { key: 'value' } }
|
|
137
|
+
return null
|
|
138
|
+
})
|
|
139
|
+
expect(loadFileToJSON('config.mjs')).toEqual({ key: 'value' })
|
|
140
|
+
})
|
|
141
|
+
|
|
142
|
+
it('should fall back to JSON.parse when require returns empty', () => {
|
|
143
|
+
makeFilesExist(['/mock/project/package.json', '/mock/project/data.json'])
|
|
144
|
+
mockRequireFn.mockImplementation((p: string) => {
|
|
145
|
+
if (p === '/mock/project/data.json') throw new Error('require failed')
|
|
146
|
+
return null
|
|
147
|
+
})
|
|
148
|
+
mockReadFileSync.mockReturnValue('{"parsed": true}')
|
|
149
|
+
expect(loadFileToJSON('data.json')).toEqual({ parsed: true })
|
|
113
150
|
})
|
|
114
151
|
|
|
115
152
|
it('should return empty object when require throws', () => {
|
|
116
|
-
|
|
153
|
+
makeFilesExist(['/mock/project/package.json', '/mock/project/bad.json'])
|
|
117
154
|
mockRequireFn.mockImplementation(() => {
|
|
118
155
|
throw new Error('require failed')
|
|
119
156
|
})
|
|
157
|
+
mockReadFileSync.mockImplementation(() => {
|
|
158
|
+
throw new Error('read failed')
|
|
159
|
+
})
|
|
120
160
|
expect(loadFileToJSON('bad.json')).toEqual({})
|
|
121
161
|
})
|
|
122
162
|
})
|
|
@@ -134,71 +174,145 @@ describe('tools-core', () => {
|
|
|
134
174
|
})
|
|
135
175
|
|
|
136
176
|
it('should return a function that gets a nested config value', () => {
|
|
137
|
-
|
|
138
|
-
mockRequireFn.
|
|
177
|
+
makeFilesExist(['/mock/project/package.json', '/mock/project/config.js'])
|
|
178
|
+
mockRequireFn.mockImplementation((p: string) => {
|
|
179
|
+
if (p === '/mock/project/config.js')
|
|
180
|
+
return { build: { sourceDir: 'src' } }
|
|
181
|
+
return null
|
|
182
|
+
})
|
|
139
183
|
const getParam = loadConfigParam('config.js')
|
|
140
184
|
expect(getParam('build.sourceDir')).toBe('src')
|
|
141
185
|
})
|
|
142
186
|
|
|
143
187
|
it('should return defaultValue when key is not found', () => {
|
|
144
|
-
|
|
145
|
-
mockRequireFn.
|
|
188
|
+
makeFilesExist(['/mock/project/package.json', '/mock/project/config.js'])
|
|
189
|
+
mockRequireFn.mockImplementation((p: string) => {
|
|
190
|
+
if (p === '/mock/project/config.js') return {}
|
|
191
|
+
return null
|
|
192
|
+
})
|
|
146
193
|
const getParam = loadConfigParam('config.js')
|
|
147
194
|
expect(getParam('missing.key', 'default')).toBe('default')
|
|
148
195
|
})
|
|
149
196
|
})
|
|
150
197
|
|
|
151
198
|
describe('loadVLToolsConfig', () => {
|
|
152
|
-
let loadVLToolsConfig: () => (key: string) => any
|
|
153
|
-
|
|
154
199
|
beforeEach(async () => {
|
|
155
200
|
vi.resetModules()
|
|
156
201
|
setupDefaultMocks()
|
|
157
|
-
const mod = await import('./index.js')
|
|
158
|
-
loadVLToolsConfig = mod.loadVLToolsConfig
|
|
159
202
|
})
|
|
160
203
|
|
|
161
|
-
it('should return a function that provides .config, .get(), .merge()', () => {
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
204
|
+
it('should return a function that provides .config, .get(), .merge()', async () => {
|
|
205
|
+
makeFilesExist([
|
|
206
|
+
'/mock/project/package.json',
|
|
207
|
+
'/mock/project/vl-tools.config.js',
|
|
208
|
+
])
|
|
209
|
+
mockRequireFn.mockImplementation((p: string) => {
|
|
210
|
+
if (p === '/mock/project/package.json') return { name: 'mock-pkg' }
|
|
211
|
+
if (p === '/mock/project/vl-tools.config.js')
|
|
212
|
+
return { build: { sourceDir: 'src' } }
|
|
213
|
+
return null
|
|
214
|
+
})
|
|
215
|
+
|
|
216
|
+
const mod = await import('./index.js')
|
|
217
|
+
const vlConfig = mod.loadVLToolsConfig()
|
|
165
218
|
const buildConfig = vlConfig('build')
|
|
166
219
|
expect(buildConfig.config).toEqual({ sourceDir: 'src' })
|
|
167
220
|
expect(buildConfig.get('sourceDir')).toBe('src')
|
|
168
221
|
})
|
|
169
222
|
|
|
170
|
-
it('should support chained merge calls', () => {
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
223
|
+
it('should support chained merge calls', async () => {
|
|
224
|
+
makeFilesExist([
|
|
225
|
+
'/mock/project/package.json',
|
|
226
|
+
'/mock/project/vl-tools.config.js',
|
|
227
|
+
])
|
|
228
|
+
mockRequireFn.mockImplementation((p: string) => {
|
|
229
|
+
if (p === '/mock/project/package.json') return { name: 'mock-pkg' }
|
|
230
|
+
if (p === '/mock/project/vl-tools.config.js')
|
|
231
|
+
return { build: { sourceDir: 'src' } }
|
|
232
|
+
return null
|
|
233
|
+
})
|
|
234
|
+
|
|
235
|
+
const mod = await import('./index.js')
|
|
236
|
+
const vlConfig = mod.loadVLToolsConfig()
|
|
174
237
|
const merged = vlConfig('build').merge({ outputDir: 'lib' })
|
|
175
238
|
expect(merged.config).toEqual({ sourceDir: 'src', outputDir: 'lib' })
|
|
176
239
|
})
|
|
177
240
|
|
|
178
|
-
it('should return empty config when file is not found', () => {
|
|
179
|
-
|
|
180
|
-
const vlConfig = loadVLToolsConfig()
|
|
241
|
+
it('should return empty config when file is not found', async () => {
|
|
242
|
+
const mod = await import('./index.js')
|
|
243
|
+
const vlConfig = mod.loadVLToolsConfig()
|
|
181
244
|
const result = vlConfig('build')
|
|
182
245
|
expect(result.config).toEqual({})
|
|
183
246
|
})
|
|
184
247
|
|
|
185
|
-
it('should return default value with get when key is missing', () => {
|
|
186
|
-
|
|
187
|
-
const vlConfig = loadVLToolsConfig()
|
|
248
|
+
it('should return default value with get when key is missing', async () => {
|
|
249
|
+
const mod = await import('./index.js')
|
|
250
|
+
const vlConfig = mod.loadVLToolsConfig()
|
|
188
251
|
const result = vlConfig('build')
|
|
189
252
|
expect(result.get('missing')).toEqual({})
|
|
190
253
|
})
|
|
254
|
+
|
|
255
|
+
it('should prefer .mjs over .js config files', async () => {
|
|
256
|
+
makeFilesExist([
|
|
257
|
+
'/mock/project/package.json',
|
|
258
|
+
'/mock/project/vl-tools.config.mjs',
|
|
259
|
+
'/mock/project/vl-tools.config.js',
|
|
260
|
+
])
|
|
261
|
+
mockRequireFn.mockImplementation((p: string) => {
|
|
262
|
+
if (p === '/mock/project/package.json') return { name: 'mock-pkg' }
|
|
263
|
+
if (p === '/mock/project/vl-tools.config.mjs')
|
|
264
|
+
return { build: { sourceDir: 'mjs-src' } }
|
|
265
|
+
if (p === '/mock/project/vl-tools.config.js')
|
|
266
|
+
return { build: { sourceDir: 'js-src' } }
|
|
267
|
+
return null
|
|
268
|
+
})
|
|
269
|
+
|
|
270
|
+
const mod = await import('./index.js')
|
|
271
|
+
const vlConfig = mod.loadVLToolsConfig()
|
|
272
|
+
expect(vlConfig('build').config).toEqual({ sourceDir: 'mjs-src' })
|
|
273
|
+
})
|
|
274
|
+
|
|
275
|
+
it('should cascade configs from root to package (deep merge)', async () => {
|
|
276
|
+
vi.spyOn(process, 'cwd').mockReturnValue('/mock/packages/ui')
|
|
277
|
+
makeFilesExist([
|
|
278
|
+
'/mock/packages/ui/package.json',
|
|
279
|
+
'/mock/vl-tools.config.js',
|
|
280
|
+
'/mock/packages/ui/vl-tools.config.js',
|
|
281
|
+
])
|
|
282
|
+
mockRequireFn.mockImplementation((p: string) => {
|
|
283
|
+
if (p === '/mock/packages/ui/package.json') return { name: 'mock-pkg' }
|
|
284
|
+
// Root config: base settings
|
|
285
|
+
if (p === '/mock/vl-tools.config.js')
|
|
286
|
+
return {
|
|
287
|
+
stories: { framework: 'vite', port: 6006 },
|
|
288
|
+
build: { sourceDir: 'src' },
|
|
289
|
+
}
|
|
290
|
+
// Package config: override framework only
|
|
291
|
+
if (p === '/mock/packages/ui/vl-tools.config.js')
|
|
292
|
+
return { stories: { framework: 'next' } }
|
|
293
|
+
return null
|
|
294
|
+
})
|
|
295
|
+
|
|
296
|
+
const mod = await import('./index.js')
|
|
297
|
+
const vlConfig = mod.loadVLToolsConfig()
|
|
298
|
+
const stories = vlConfig('stories')
|
|
299
|
+
|
|
300
|
+
// framework overridden by package config
|
|
301
|
+
expect(stories.get('framework')).toBe('next')
|
|
302
|
+
// port inherited from root config
|
|
303
|
+
expect(stories.get('port')).toBe(6006)
|
|
304
|
+
// build inherited from root config
|
|
305
|
+
expect(vlConfig('build').config).toEqual({ sourceDir: 'src' })
|
|
306
|
+
})
|
|
191
307
|
})
|
|
192
308
|
|
|
193
309
|
describe('module-level constants', () => {
|
|
194
310
|
it('should export PKG with bundleName from scoped package name', async () => {
|
|
195
311
|
vi.resetModules()
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
mockRequireFn.mockImplementation((path: string) => {
|
|
201
|
-
if (path === '/path/to/package.json') {
|
|
312
|
+
vi.spyOn(process, 'cwd').mockReturnValue('/path/to')
|
|
313
|
+
makeFilesExist(['/path/to/package.json'])
|
|
314
|
+
mockRequireFn.mockImplementation((p: string) => {
|
|
315
|
+
if (p === '/path/to/package.json') {
|
|
202
316
|
return {
|
|
203
317
|
name: '@test/pkg',
|
|
204
318
|
version: '1.0.0',
|
|
@@ -216,12 +330,10 @@ describe('tools-core', () => {
|
|
|
216
330
|
|
|
217
331
|
it('should handle simple hyphenated package names in bundleName', async () => {
|
|
218
332
|
vi.resetModules()
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
mockRequireFn.mockImplementation((path: string) => {
|
|
224
|
-
if (path === '/path/to/package.json') {
|
|
333
|
+
vi.spyOn(process, 'cwd').mockReturnValue('/path/to')
|
|
334
|
+
makeFilesExist(['/path/to/package.json'])
|
|
335
|
+
mockRequireFn.mockImplementation((p: string) => {
|
|
336
|
+
if (p === '/path/to/package.json') {
|
|
225
337
|
return { name: 'my-cool-library' }
|
|
226
338
|
}
|
|
227
339
|
return null
|
|
@@ -233,12 +345,10 @@ describe('tools-core', () => {
|
|
|
233
345
|
|
|
234
346
|
it('should include peerDependencies in externalDependencies', async () => {
|
|
235
347
|
vi.resetModules()
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
mockRequireFn.mockImplementation((path: string) => {
|
|
241
|
-
if (path === '/path/to/package.json') {
|
|
348
|
+
vi.spyOn(process, 'cwd').mockReturnValue('/path/to')
|
|
349
|
+
makeFilesExist(['/path/to/package.json'])
|
|
350
|
+
mockRequireFn.mockImplementation((p: string) => {
|
|
351
|
+
if (p === '/path/to/package.json') {
|
|
242
352
|
return {
|
|
243
353
|
name: 'my-lib',
|
|
244
354
|
peerDependencies: { 'styled-components': '^6' },
|
|
@@ -260,14 +370,14 @@ describe('tools-core', () => {
|
|
|
260
370
|
|
|
261
371
|
it('should export TS_CONFIG from tsconfig.json', async () => {
|
|
262
372
|
vi.resetModules()
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
mockRequireFn.mockImplementation((
|
|
269
|
-
if (
|
|
270
|
-
if (
|
|
373
|
+
vi.spyOn(process, 'cwd').mockReturnValue('/mock/project')
|
|
374
|
+
makeFilesExist([
|
|
375
|
+
'/mock/project/package.json',
|
|
376
|
+
'/mock/project/tsconfig.json',
|
|
377
|
+
])
|
|
378
|
+
mockRequireFn.mockImplementation((p: string) => {
|
|
379
|
+
if (p === '/mock/project/package.json') return { name: 'mock-pkg' }
|
|
380
|
+
if (p === '/mock/project/tsconfig.json') {
|
|
271
381
|
return { compilerOptions: { strict: true } }
|
|
272
382
|
}
|
|
273
383
|
return null
|
package/src/index.ts
CHANGED
|
@@ -1,47 +1,110 @@
|
|
|
1
1
|
import fs from 'node:fs'
|
|
2
2
|
import { createRequire } from 'node:module'
|
|
3
|
-
import
|
|
4
|
-
import { get as _get, merge } from 'lodash-es'
|
|
3
|
+
import path from 'node:path'
|
|
5
4
|
|
|
6
|
-
const
|
|
5
|
+
const VL_CONFIG_FILES = ['vl-tools.config.mjs', 'vl-tools.config.js']
|
|
7
6
|
const PACKAGE_FILE_NAME = 'package.json'
|
|
8
7
|
const TYPESCRIPT_FILE_NAME = 'tsconfig.json'
|
|
9
8
|
|
|
10
9
|
const require = createRequire(import.meta.url)
|
|
11
10
|
|
|
11
|
+
// --------------------------------------------------------
|
|
12
|
+
// Utility helpers (replaces lodash-es and find-up)
|
|
13
|
+
// --------------------------------------------------------
|
|
14
|
+
const get = (obj: any, dotPath: string, defaultValue: any = {}): any => {
|
|
15
|
+
const keys = dotPath.split('.')
|
|
16
|
+
let result = obj
|
|
17
|
+
|
|
18
|
+
for (const key of keys) {
|
|
19
|
+
if (result == null) return defaultValue
|
|
20
|
+
result = result[key]
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return result === undefined ? defaultValue : result
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const deepMerge = (
|
|
27
|
+
target: Record<string, any>,
|
|
28
|
+
source: Record<string, any>,
|
|
29
|
+
): Record<string, any> => {
|
|
30
|
+
const result = { ...target }
|
|
31
|
+
|
|
32
|
+
for (const key of Object.keys(source)) {
|
|
33
|
+
const srcVal = source[key]
|
|
34
|
+
const tgtVal = result[key]
|
|
35
|
+
|
|
36
|
+
if (
|
|
37
|
+
typeof srcVal === 'object' &&
|
|
38
|
+
srcVal !== null &&
|
|
39
|
+
!Array.isArray(srcVal) &&
|
|
40
|
+
typeof tgtVal === 'object' &&
|
|
41
|
+
tgtVal !== null &&
|
|
42
|
+
!Array.isArray(tgtVal)
|
|
43
|
+
) {
|
|
44
|
+
result[key] = deepMerge(tgtVal, srcVal)
|
|
45
|
+
} else {
|
|
46
|
+
result[key] = srcVal
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return result
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const findFileUp = (
|
|
54
|
+
names: string | string[],
|
|
55
|
+
cwd = process.cwd(),
|
|
56
|
+
): string | undefined => {
|
|
57
|
+
const fileNames = Array.isArray(names) ? names : [names]
|
|
58
|
+
let dir = path.resolve(cwd)
|
|
59
|
+
|
|
60
|
+
while (true) {
|
|
61
|
+
for (const name of fileNames) {
|
|
62
|
+
const filePath = path.join(dir, name)
|
|
63
|
+
try {
|
|
64
|
+
if (fs.statSync(filePath).isFile()) return filePath
|
|
65
|
+
} catch (_e) {
|
|
66
|
+
// file doesn't exist, continue
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const parent = path.dirname(dir)
|
|
71
|
+
if (parent === dir) return undefined
|
|
72
|
+
dir = parent
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
12
76
|
// --------------------------------------------------------
|
|
13
77
|
// FIND & READ file helpers
|
|
14
78
|
// --------------------------------------------------------
|
|
15
|
-
const findFile = (filename: string) =>
|
|
79
|
+
const findFile = (filename: string) => findFileUp(filename)
|
|
80
|
+
|
|
81
|
+
const loadModule = (filePath: string): Record<string, any> => {
|
|
82
|
+
try {
|
|
83
|
+
const imported = require(filePath)
|
|
84
|
+
// Handle ESM default export wrapping
|
|
85
|
+
return imported?.default ?? imported ?? {}
|
|
86
|
+
} catch (_e) {
|
|
87
|
+
return {}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
16
90
|
|
|
17
91
|
const loadFileToJSON = (filename: string): Record<string, any> => {
|
|
18
92
|
const file = findFile(filename)
|
|
19
93
|
|
|
20
94
|
if (!file) return {}
|
|
21
95
|
|
|
22
|
-
let data: Record<string, any> = {}
|
|
23
|
-
|
|
24
96
|
// try to read an exported module first
|
|
25
|
-
|
|
26
|
-
|
|
97
|
+
const data = loadModule(file)
|
|
98
|
+
if (data && Object.keys(data).length > 0) return data
|
|
27
99
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
100
|
+
// try to read a plain json file like tsconfig.json
|
|
101
|
+
try {
|
|
102
|
+
return JSON.parse(fs.readFileSync(file, 'utf-8'))
|
|
31
103
|
} catch (_e) {
|
|
32
104
|
// ignore error
|
|
33
105
|
}
|
|
34
106
|
|
|
35
|
-
|
|
36
|
-
if (!data) {
|
|
37
|
-
try {
|
|
38
|
-
data = JSON.parse(fs.readFileSync(file, 'utf-8'))
|
|
39
|
-
} catch (_e) {
|
|
40
|
-
// ignore error
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
return data
|
|
107
|
+
return {}
|
|
45
108
|
}
|
|
46
109
|
|
|
47
110
|
// --------------------------------------------------------
|
|
@@ -70,10 +133,6 @@ const getDependenciesList = (types: any) => {
|
|
|
70
133
|
return result
|
|
71
134
|
}
|
|
72
135
|
|
|
73
|
-
// parse namespace name
|
|
74
|
-
// const parseNamespace = (name) =>
|
|
75
|
-
// name.startsWith('@') ? name.split('/')[0] : ''
|
|
76
|
-
|
|
77
136
|
// converts package name to umd or iife valid format
|
|
78
137
|
// example: napespace-package-name => namespacePackageName
|
|
79
138
|
const camelspaceBundleName = (name: string) => {
|
|
@@ -96,14 +155,9 @@ const camelspaceBundleName = (name: string) => {
|
|
|
96
155
|
const getPkgData = (): Record<string, any> => {
|
|
97
156
|
const pkg = getPackageJSON()
|
|
98
157
|
const { name } = pkg
|
|
99
|
-
// const namespace = parseNamespace(name)
|
|
100
158
|
|
|
101
159
|
return {
|
|
102
160
|
...pkg,
|
|
103
|
-
// nameWithoutPrefix: name.replace(namespace, '').replace('/', ''),
|
|
104
|
-
// namespace,
|
|
105
|
-
// namespaceName: namespace.replace('@', ''),
|
|
106
|
-
// rootPath: findFilePath('package.json'),
|
|
107
161
|
bundleName: camelspaceBundleName(name),
|
|
108
162
|
externalDependencies: getDependenciesList([
|
|
109
163
|
'dependencies',
|
|
@@ -114,15 +168,45 @@ const getPkgData = (): Record<string, any> => {
|
|
|
114
168
|
|
|
115
169
|
// --------------------------------------------------------
|
|
116
170
|
// LOAD EXTERNAL CONFIGURATION
|
|
171
|
+
// Cascading: finds all vl-tools.config.{mjs,js} files from
|
|
172
|
+
// cwd upward, then deep-merges them (root first, closest
|
|
173
|
+
// package config wins).
|
|
117
174
|
// --------------------------------------------------------
|
|
118
|
-
const
|
|
175
|
+
const findAllConfigFiles = (): string[] => {
|
|
176
|
+
const files: string[] = []
|
|
177
|
+
let cwd = process.cwd()
|
|
178
|
+
|
|
179
|
+
while (true) {
|
|
180
|
+
const file = findFileUp(VL_CONFIG_FILES, cwd)
|
|
181
|
+
if (!file) break
|
|
182
|
+
files.push(file)
|
|
183
|
+
const parentDir = path.dirname(path.dirname(file))
|
|
184
|
+
if (parentDir === path.dirname(file)) break
|
|
185
|
+
cwd = parentDir
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// Root config first, closest config last (overrides)
|
|
189
|
+
return files.reverse()
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
const getExternalConfig = (): Record<string, any> => {
|
|
193
|
+
const files = findAllConfigFiles()
|
|
194
|
+
let config: Record<string, any> = {}
|
|
195
|
+
|
|
196
|
+
for (const file of files) {
|
|
197
|
+
const loaded = loadModule(file)
|
|
198
|
+
config = deepMerge(config, loaded)
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
return config
|
|
202
|
+
}
|
|
119
203
|
|
|
120
204
|
const loadConfigParam =
|
|
121
205
|
(filename: string) =>
|
|
122
206
|
(key: string, defaultValue = {}) => {
|
|
123
207
|
const externalConfig = loadFileToJSON(filename)
|
|
124
208
|
|
|
125
|
-
return
|
|
209
|
+
return get(externalConfig, key, defaultValue)
|
|
126
210
|
}
|
|
127
211
|
|
|
128
212
|
const loadVLToolsConfig = () => {
|
|
@@ -133,13 +217,13 @@ const loadVLToolsConfig = () => {
|
|
|
133
217
|
return object
|
|
134
218
|
},
|
|
135
219
|
get: (param: string, defaultValue?: any) =>
|
|
136
|
-
|
|
220
|
+
get(object, param, defaultValue || {}),
|
|
137
221
|
merge: (param: Record<string, any>) =>
|
|
138
|
-
cloneAndEnhance(
|
|
222
|
+
cloneAndEnhance(deepMerge(param, object)),
|
|
139
223
|
})
|
|
140
224
|
|
|
141
225
|
const getOutput = (key: string) => {
|
|
142
|
-
const result =
|
|
226
|
+
const result = get(externalConfig, key, {})
|
|
143
227
|
|
|
144
228
|
return cloneAndEnhance(result)
|
|
145
229
|
}
|
|
@@ -147,6 +231,8 @@ const loadVLToolsConfig = () => {
|
|
|
147
231
|
return getOutput
|
|
148
232
|
}
|
|
149
233
|
|
|
234
|
+
const defineConfig = <T extends Record<string, any>>(config: T): T => config
|
|
235
|
+
|
|
150
236
|
const swapGlobals = (globals: Record<string, string>) =>
|
|
151
237
|
Object.entries(globals).reduce<Record<string, string>>(
|
|
152
238
|
(acc, [key, value]) => {
|
|
@@ -161,6 +247,7 @@ const VL_CONFIG = loadVLToolsConfig()
|
|
|
161
247
|
const TS_CONFIG = loadFileToJSON(TYPESCRIPT_FILE_NAME)
|
|
162
248
|
|
|
163
249
|
export {
|
|
250
|
+
defineConfig,
|
|
164
251
|
findFile,
|
|
165
252
|
loadConfigParam,
|
|
166
253
|
loadFileToJSON,
|