@vitus-labs/tools-core 1.8.1-alpha.2 → 1.9.0

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 CHANGED
@@ -1,10 +1,9 @@
1
1
  import fs from 'node:fs';
2
- import { createRequire } from 'node:module';
3
2
  import path from 'node:path';
4
- const VL_CONFIG_FILES = ['vl-tools.config.mjs', 'vl-tools.config.js'];
3
+ import { pathToFileURL } from 'node:url';
4
+ const VL_CONFIG_FILES = ['vl-tools.config.mjs'];
5
5
  const PACKAGE_FILE_NAME = 'package.json';
6
6
  const TYPESCRIPT_FILE_NAME = 'tsconfig.json';
7
- const require = createRequire(import.meta.url);
8
7
  // --------------------------------------------------------
9
8
  // Utility helpers (replaces lodash-es and find-up)
10
9
  // --------------------------------------------------------
@@ -61,40 +60,31 @@ const findFileUp = (names, cwd = process.cwd()) => {
61
60
  // FIND & READ file helpers
62
61
  // --------------------------------------------------------
63
62
  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
- };
74
63
  const loadFileToJSON = (filename) => {
75
64
  const file = findFile(filename);
76
65
  if (!file)
77
66
  return {};
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
83
67
  try {
84
68
  return JSON.parse(fs.readFileSync(file, 'utf-8'));
85
69
  }
86
70
  catch (_e) {
87
- // ignore error
71
+ return {};
72
+ }
73
+ };
74
+ const loadModuleAsync = async (filePath) => {
75
+ try {
76
+ const mod = await import(pathToFileURL(filePath).href);
77
+ return mod?.default ?? mod ?? {};
78
+ }
79
+ catch (e) {
80
+ console.warn(`[tools-core] Failed to load config: ${filePath}\n ${e.message}`);
81
+ return {};
88
82
  }
89
- return {};
90
83
  };
91
84
  // --------------------------------------------------------
92
85
  // GET PACKAGE.JSON info
93
86
  // --------------------------------------------------------
94
- const getPackageJSON = () => {
95
- const data = loadFileToJSON(PACKAGE_FILE_NAME);
96
- return data;
97
- };
87
+ const getPackageJSON = () => loadFileToJSON(PACKAGE_FILE_NAME);
98
88
  // --------------------------------------------------------
99
89
  // PACKAGE.json parsing functions
100
90
  // --------------------------------------------------------
@@ -136,7 +126,7 @@ const getPkgData = () => {
136
126
  };
137
127
  // --------------------------------------------------------
138
128
  // LOAD EXTERNAL CONFIGURATION
139
- // Cascading: finds all vl-tools.config.{mjs,js} files from
129
+ // Cascading: finds all vl-tools.config.mjs files from
140
130
  // cwd upward, then deep-merges them (root first, closest
141
131
  // package config wins).
142
132
  // --------------------------------------------------------
@@ -156,11 +146,11 @@ const findAllConfigFiles = () => {
156
146
  // Root config first, closest config last (overrides)
157
147
  return files.reverse();
158
148
  };
159
- const getExternalConfig = () => {
149
+ const getExternalConfig = async () => {
160
150
  const files = findAllConfigFiles();
161
151
  let config = {};
162
152
  for (const file of files) {
163
- const loaded = loadModule(file);
153
+ const loaded = await loadModuleAsync(file);
164
154
  config = deepMerge(config, loaded);
165
155
  }
166
156
  return config;
@@ -169,8 +159,8 @@ const loadConfigParam = (filename) => (key, defaultValue = {}) => {
169
159
  const externalConfig = loadFileToJSON(filename);
170
160
  return get(externalConfig, key, defaultValue);
171
161
  };
172
- const loadVLToolsConfig = () => {
173
- const externalConfig = getExternalConfig();
162
+ const loadVLToolsConfig = async () => {
163
+ const externalConfig = await getExternalConfig();
174
164
  const cloneAndEnhance = (object) => ({
175
165
  get config() {
176
166
  return object;
@@ -190,7 +180,7 @@ const swapGlobals = (globals) => Object.entries(globals).reduce((acc, [key, valu
190
180
  return acc;
191
181
  }, {});
192
182
  const PKG = getPkgData();
193
- const VL_CONFIG = loadVLToolsConfig();
183
+ const VL_CONFIG = await loadVLToolsConfig();
194
184
  const TS_CONFIG = loadFileToJSON(TYPESCRIPT_FILE_NAME);
195
185
  export { defineConfig, findFile, loadConfigParam, loadFileToJSON, loadVLToolsConfig, swapGlobals, PKG, VL_CONFIG, TS_CONFIG, };
196
186
  //# 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":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -1,11 +1,11 @@
1
1
  declare const findFile: (filename: string) => string | undefined;
2
2
  declare const loadFileToJSON: (filename: string) => Record<string, any>;
3
3
  declare const loadConfigParam: (filename: string) => (key: string, defaultValue?: {}) => any;
4
- declare const loadVLToolsConfig: () => (key: string) => {
4
+ declare const loadVLToolsConfig: () => Promise<(key: string) => {
5
5
  readonly config: Record<string, any>;
6
6
  get: (param: string, defaultValue?: any) => any;
7
7
  merge: (param: Record<string, any>) => /*elided*/ any;
8
- };
8
+ }>;
9
9
  declare const defineConfig: <T extends Record<string, any>>(config: T) => T;
10
10
  declare const swapGlobals: (globals: Record<string, string>) => Record<string, string>;
11
11
  declare const PKG: Record<string, any>;
@@ -1 +1 @@
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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AA4EA,QAAA,MAAM,QAAQ,GAAI,UAAU,MAAM,uBAAyB,CAAA;AAE3D,QAAA,MAAM,cAAc,GAAI,UAAU,MAAM,KAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAS5D,CAAA;AA0GD,QAAA,MAAM,eAAe,GAClB,UAAU,MAAM,MAChB,KAAK,MAAM,EAAE,iBAAiB,QAI9B,CAAA;AAEH,QAAA,MAAM,iBAAiB,sBAaG,MAAM;;iBANf,MAAM,iBAAiB,GAAG;mBAExB,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC;EAWrC,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;CAyBK,CAAA;AAC3C,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.2+6f381e3",
3
+ "version": "1.9.0",
4
4
  "license": "MIT",
5
5
  "repository": {
6
6
  "type": "git",
@@ -30,8 +30,8 @@
30
30
  "typecheck": "tsc --noEmit"
31
31
  },
32
32
  "devDependencies": {
33
- "@vitus-labs/tools-typescript": "1.8.1-alpha.2+6f381e3",
33
+ "@vitus-labs/tools-typescript": "1.9.0",
34
34
  "typescript": "^5.9.3"
35
35
  },
36
- "gitHead": "6f381e35e5829bc1fae660817f19bfa5098674d6"
36
+ "gitHead": "167c6e3a00ad6e9bcec14dfdb7aaef3ffbdb42e9"
37
37
  }
package/src/index.test.ts CHANGED
@@ -1,300 +1,260 @@
1
- import { beforeEach, describe, expect, it, vi } from 'vitest'
2
-
3
- const mockStatSync = vi.fn()
4
- const mockReadFileSync = vi.fn()
5
- vi.mock('node:fs', () => ({
6
- default: {
7
- statSync: mockStatSync,
8
- readFileSync: mockReadFileSync,
9
- },
10
- statSync: mockStatSync,
11
- readFileSync: mockReadFileSync,
12
- }))
13
-
14
- const mockRequireFn = vi.fn()
15
- vi.mock('node:module', () => ({
16
- createRequire: vi.fn(() => mockRequireFn),
17
- }))
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
- })
1
+ import { mkdirSync, rmSync, writeFileSync } from 'node:fs'
2
+ import { tmpdir } from 'node:os'
3
+ import path from 'node:path'
4
+ import { afterAll, beforeEach, describe, expect, it, vi } from 'vitest'
5
+
6
+ const BASE_TMP = path.join(tmpdir(), `vl-core-test-${Date.now()}`)
7
+ let testId = 0
8
+
9
+ const createTestDir = (opts: {
10
+ packageJson?: Record<string, any>
11
+ tsConfig?: Record<string, any>
12
+ vlConfig?: Record<string, any>
13
+ parent?: { vlConfig?: Record<string, any> }
14
+ }) => {
15
+ testId++
16
+ const root = path.join(BASE_TMP, `t${testId}`)
17
+ const projectDir = opts.parent ? path.join(root, 'packages', 'child') : root
18
+
19
+ mkdirSync(projectDir, { recursive: true })
20
+
21
+ writeFileSync(
22
+ path.join(projectDir, 'package.json'),
23
+ JSON.stringify(opts.packageJson ?? { name: 'test-pkg' }),
24
+ )
25
+
26
+ if (opts.tsConfig) {
27
+ writeFileSync(
28
+ path.join(projectDir, 'tsconfig.json'),
29
+ JSON.stringify(opts.tsConfig),
30
+ )
31
+ }
32
+
33
+ if (opts.vlConfig) {
34
+ writeFileSync(
35
+ path.join(projectDir, 'vl-tools.config.mjs'),
36
+ `export default ${JSON.stringify(opts.vlConfig)}`,
37
+ )
38
+ }
39
+
40
+ if (opts.parent?.vlConfig) {
41
+ writeFileSync(
42
+ path.join(root, 'vl-tools.config.mjs'),
43
+ `export default ${JSON.stringify(opts.parent.vlConfig)}`,
44
+ )
45
+ }
46
+
47
+ return projectDir
25
48
  }
26
49
 
27
- // Default mock setup that satisfies module-level getPkgData()
28
- // package.json must always be found with a valid `name` field,
29
- // otherwise camelspaceBundleName(undefined) crashes at import time.
30
- const setupDefaultMocks = () => {
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' }
35
- return null
36
- })
37
- }
50
+ afterAll(() => {
51
+ rmSync(BASE_TMP, { recursive: true, force: true })
52
+ })
38
53
 
39
54
  describe('tools-core', () => {
40
55
  beforeEach(() => {
41
- mockStatSync.mockReset()
42
- mockReadFileSync.mockReset()
43
- mockRequireFn.mockReset()
44
56
  vi.restoreAllMocks()
45
57
  })
46
58
 
47
59
  describe('swapGlobals', () => {
48
- let swapGlobals: (globals: Record<string, string>) => Record<string, string>
49
-
50
- beforeEach(async () => {
60
+ it('should invert key/value pairs', async () => {
51
61
  vi.resetModules()
52
- setupDefaultMocks()
53
- const mod = await import('./index.js')
54
- swapGlobals = mod.swapGlobals
55
- })
62
+ const dir = createTestDir({})
63
+ vi.spyOn(process, 'cwd').mockReturnValue(dir)
56
64
 
57
- it('should invert key/value pairs', () => {
65
+ const mod = await import('./index.js')
58
66
  const input = { react: 'React', 'react-dom': 'ReactDOM' }
59
- const result = swapGlobals(input)
60
- expect(result).toEqual({ React: 'react', ReactDOM: 'react-dom' })
67
+ expect(mod.swapGlobals(input)).toEqual({
68
+ React: 'react',
69
+ ReactDOM: 'react-dom',
70
+ })
61
71
  })
62
72
 
63
- it('should return empty object for empty input', () => {
64
- expect(swapGlobals({})).toEqual({})
73
+ it('should return empty object for empty input', async () => {
74
+ vi.resetModules()
75
+ const dir = createTestDir({})
76
+ vi.spyOn(process, 'cwd').mockReturnValue(dir)
77
+
78
+ const mod = await import('./index.js')
79
+ expect(mod.swapGlobals({})).toEqual({})
65
80
  })
66
81
  })
67
82
 
68
83
  describe('defineConfig', () => {
69
- let defineConfig: <T extends Record<string, any>>(config: T) => T
70
-
71
- beforeEach(async () => {
84
+ it('should return the same config object', async () => {
72
85
  vi.resetModules()
73
- setupDefaultMocks()
74
- const mod = await import('./index.js')
75
- defineConfig = mod.defineConfig
76
- })
86
+ const dir = createTestDir({})
87
+ vi.spyOn(process, 'cwd').mockReturnValue(dir)
77
88
 
78
- it('should return the same config object', () => {
89
+ const mod = await import('./index.js')
79
90
  const config = { stories: { framework: 'next' } }
80
- expect(defineConfig(config)).toBe(config)
91
+ expect(mod.defineConfig(config)).toBe(config)
81
92
  })
82
93
  })
83
94
 
84
95
  describe('findFile', () => {
85
- let findFile: (filename: string) => string | undefined
86
-
87
- beforeEach(async () => {
96
+ it('should return the path when file is found in cwd', async () => {
88
97
  vi.resetModules()
89
- setupDefaultMocks()
98
+ const dir = createTestDir({ tsConfig: { strict: true } })
99
+ vi.spyOn(process, 'cwd').mockReturnValue(dir)
100
+
90
101
  const mod = await import('./index.js')
91
- findFile = mod.findFile
102
+ expect(mod.findFile('tsconfig.json')).toBe(
103
+ path.join(dir, 'tsconfig.json'),
104
+ )
92
105
  })
93
106
 
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')
97
- })
107
+ it('should walk up directories to find the file', async () => {
108
+ vi.resetModules()
109
+ testId++
110
+ const root = path.join(BASE_TMP, `t${testId}`)
111
+ const child = path.join(root, 'sub', 'deep')
112
+ mkdirSync(child, { recursive: true })
113
+ writeFileSync(
114
+ path.join(child, 'package.json'),
115
+ JSON.stringify({ name: 'deep' }),
116
+ )
117
+ writeFileSync(path.join(root, 'target.json'), '{}')
118
+ vi.spyOn(process, 'cwd').mockReturnValue(child)
98
119
 
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')
120
+ const mod = await import('./index.js')
121
+ expect(mod.findFile('target.json')).toBe(path.join(root, 'target.json'))
102
122
  })
103
123
 
104
- it('should return undefined when file is not found', () => {
105
- expect(findFile('missing.json')).toBeUndefined()
124
+ it('should return undefined when file is not found', async () => {
125
+ vi.resetModules()
126
+ const dir = createTestDir({})
127
+ vi.spyOn(process, 'cwd').mockReturnValue(dir)
128
+
129
+ const mod = await import('./index.js')
130
+ expect(mod.findFile('nonexistent.xyz')).toBeUndefined()
106
131
  })
107
132
  })
108
133
 
109
134
  describe('loadFileToJSON', () => {
110
- let loadFileToJSON: (filename: string) => Record<string, any>
111
-
112
- beforeEach(async () => {
135
+ it('should return empty object when file is not found', async () => {
113
136
  vi.resetModules()
114
- setupDefaultMocks()
115
- const mod = await import('./index.js')
116
- loadFileToJSON = mod.loadFileToJSON
117
- })
137
+ const dir = createTestDir({})
138
+ vi.spyOn(process, 'cwd').mockReturnValue(dir)
118
139
 
119
- it('should return empty object when file is not found', () => {
120
- expect(loadFileToJSON('missing.json')).toEqual({})
140
+ const mod = await import('./index.js')
141
+ expect(mod.loadFileToJSON('missing.json')).toEqual({})
121
142
  })
122
143
 
123
- it('should load file using require when available', () => {
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
144
+ it('should parse JSON files', async () => {
145
+ vi.resetModules()
146
+ const dir = createTestDir({
147
+ tsConfig: { compilerOptions: { strict: true } },
128
148
  })
129
- expect(loadFileToJSON('config.js')).toEqual({ key: 'value' })
130
- })
149
+ vi.spyOn(process, 'cwd').mockReturnValue(dir)
131
150
 
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
151
+ const mod = await import('./index.js')
152
+ expect(mod.loadFileToJSON('tsconfig.json')).toEqual({
153
+ compilerOptions: { strict: true },
138
154
  })
139
- expect(loadFileToJSON('config.mjs')).toEqual({ key: 'value' })
140
155
  })
141
156
 
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 })
150
- })
157
+ it('should return empty object for invalid JSON', async () => {
158
+ vi.resetModules()
159
+ const dir = createTestDir({})
160
+ writeFileSync(path.join(dir, 'bad.json'), 'not json{{{')
161
+ vi.spyOn(process, 'cwd').mockReturnValue(dir)
151
162
 
152
- it('should return empty object when require throws', () => {
153
- makeFilesExist(['/mock/project/package.json', '/mock/project/bad.json'])
154
- mockRequireFn.mockImplementation(() => {
155
- throw new Error('require failed')
156
- })
157
- mockReadFileSync.mockImplementation(() => {
158
- throw new Error('read failed')
159
- })
160
- expect(loadFileToJSON('bad.json')).toEqual({})
163
+ const mod = await import('./index.js')
164
+ expect(mod.loadFileToJSON('bad.json')).toEqual({})
161
165
  })
162
166
  })
163
167
 
164
168
  describe('loadConfigParam', () => {
165
- let loadConfigParam: (
166
- filename: string,
167
- ) => (key: string, defaultValue?: any) => any
168
-
169
- beforeEach(async () => {
169
+ it('should return a function that gets a nested config value', async () => {
170
170
  vi.resetModules()
171
- setupDefaultMocks()
172
- const mod = await import('./index.js')
173
- loadConfigParam = mod.loadConfigParam
174
- })
171
+ const dir = createTestDir({})
172
+ writeFileSync(
173
+ path.join(dir, 'config.json'),
174
+ JSON.stringify({ build: { sourceDir: 'src' } }),
175
+ )
176
+ vi.spyOn(process, 'cwd').mockReturnValue(dir)
175
177
 
176
- it('should return a function that gets a nested config value', () => {
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
- })
183
- const getParam = loadConfigParam('config.js')
178
+ const mod = await import('./index.js')
179
+ const getParam = mod.loadConfigParam('config.json')
184
180
  expect(getParam('build.sourceDir')).toBe('src')
185
181
  })
186
182
 
187
- it('should return defaultValue when key is not found', () => {
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
- })
193
- const getParam = loadConfigParam('config.js')
183
+ it('should return defaultValue when key is not found', async () => {
184
+ vi.resetModules()
185
+ const dir = createTestDir({})
186
+ writeFileSync(path.join(dir, 'config.json'), JSON.stringify({}))
187
+ vi.spyOn(process, 'cwd').mockReturnValue(dir)
188
+
189
+ const mod = await import('./index.js')
190
+ const getParam = mod.loadConfigParam('config.json')
194
191
  expect(getParam('missing.key', 'default')).toBe('default')
195
192
  })
196
193
  })
197
194
 
198
195
  describe('loadVLToolsConfig', () => {
199
- beforeEach(async () => {
196
+ it('should load .mjs config and provide .config, .get(), .merge()', async () => {
200
197
  vi.resetModules()
201
- setupDefaultMocks()
202
- })
203
-
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
198
+ const dir = createTestDir({
199
+ vlConfig: { build: { sourceDir: 'src' } },
214
200
  })
201
+ vi.spyOn(process, 'cwd').mockReturnValue(dir)
215
202
 
216
203
  const mod = await import('./index.js')
217
- const vlConfig = mod.loadVLToolsConfig()
204
+ const vlConfig = await mod.loadVLToolsConfig()
218
205
  const buildConfig = vlConfig('build')
219
206
  expect(buildConfig.config).toEqual({ sourceDir: 'src' })
220
207
  expect(buildConfig.get('sourceDir')).toBe('src')
221
208
  })
222
209
 
223
210
  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
211
+ vi.resetModules()
212
+ const dir = createTestDir({
213
+ vlConfig: { build: { sourceDir: 'src' } },
233
214
  })
215
+ vi.spyOn(process, 'cwd').mockReturnValue(dir)
234
216
 
235
217
  const mod = await import('./index.js')
236
- const vlConfig = mod.loadVLToolsConfig()
218
+ const vlConfig = await mod.loadVLToolsConfig()
237
219
  const merged = vlConfig('build').merge({ outputDir: 'lib' })
238
220
  expect(merged.config).toEqual({ sourceDir: 'src', outputDir: 'lib' })
239
221
  })
240
222
 
241
- it('should return empty config when file is not found', async () => {
242
- const mod = await import('./index.js')
243
- const vlConfig = mod.loadVLToolsConfig()
244
- const result = vlConfig('build')
245
- expect(result.config).toEqual({})
246
- })
223
+ it('should return empty config when no config file exists', async () => {
224
+ vi.resetModules()
225
+ const dir = createTestDir({})
226
+ vi.spyOn(process, 'cwd').mockReturnValue(dir)
247
227
 
248
- it('should return default value with get when key is missing', async () => {
249
228
  const mod = await import('./index.js')
250
- const vlConfig = mod.loadVLToolsConfig()
251
- const result = vlConfig('build')
252
- expect(result.get('missing')).toEqual({})
229
+ const vlConfig = await mod.loadVLToolsConfig()
230
+ expect(vlConfig('build').config).toEqual({})
253
231
  })
254
232
 
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
- })
233
+ it('should return default value with get when key is missing', async () => {
234
+ vi.resetModules()
235
+ const dir = createTestDir({})
236
+ vi.spyOn(process, 'cwd').mockReturnValue(dir)
269
237
 
270
238
  const mod = await import('./index.js')
271
- const vlConfig = mod.loadVLToolsConfig()
272
- expect(vlConfig('build').config).toEqual({ sourceDir: 'mjs-src' })
239
+ const vlConfig = await mod.loadVLToolsConfig()
240
+ expect(vlConfig('build').get('missing')).toEqual({})
273
241
  })
274
242
 
275
243
  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 {
244
+ vi.resetModules()
245
+ const dir = createTestDir({
246
+ vlConfig: { stories: { framework: 'next' } },
247
+ parent: {
248
+ vlConfig: {
287
249
  stories: { framework: 'vite', port: 6006 },
288
250
  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
251
+ },
252
+ },
294
253
  })
254
+ vi.spyOn(process, 'cwd').mockReturnValue(dir)
295
255
 
296
256
  const mod = await import('./index.js')
297
- const vlConfig = mod.loadVLToolsConfig()
257
+ const vlConfig = await mod.loadVLToolsConfig()
298
258
  const stories = vlConfig('stories')
299
259
 
300
260
  // framework overridden by package config
@@ -309,18 +269,14 @@ describe('tools-core', () => {
309
269
  describe('module-level constants', () => {
310
270
  it('should export PKG with bundleName from scoped package name', async () => {
311
271
  vi.resetModules()
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') {
316
- return {
317
- name: '@test/pkg',
318
- version: '1.0.0',
319
- dependencies: { react: '^19' },
320
- }
321
- }
322
- return null
272
+ const dir = createTestDir({
273
+ packageJson: {
274
+ name: '@test/pkg',
275
+ version: '1.0.0',
276
+ dependencies: { react: '^19' },
277
+ },
323
278
  })
279
+ vi.spyOn(process, 'cwd').mockReturnValue(dir)
324
280
 
325
281
  const mod = await import('./index.js')
326
282
  expect(mod.PKG.name).toBe('@test/pkg')
@@ -330,14 +286,10 @@ describe('tools-core', () => {
330
286
 
331
287
  it('should handle simple hyphenated package names in bundleName', async () => {
332
288
  vi.resetModules()
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') {
337
- return { name: 'my-cool-library' }
338
- }
339
- return null
289
+ const dir = createTestDir({
290
+ packageJson: { name: 'my-cool-library' },
340
291
  })
292
+ vi.spyOn(process, 'cwd').mockReturnValue(dir)
341
293
 
342
294
  const mod = await import('./index.js')
343
295
  expect(mod.PKG.bundleName).toBe('myCoolLibrary')
@@ -345,17 +297,13 @@ describe('tools-core', () => {
345
297
 
346
298
  it('should include peerDependencies in externalDependencies', async () => {
347
299
  vi.resetModules()
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') {
352
- return {
353
- name: 'my-lib',
354
- peerDependencies: { 'styled-components': '^6' },
355
- }
356
- }
357
- return null
300
+ const dir = createTestDir({
301
+ packageJson: {
302
+ name: 'my-lib',
303
+ peerDependencies: { 'styled-components': '^6' },
304
+ },
358
305
  })
306
+ vi.spyOn(process, 'cwd').mockReturnValue(dir)
359
307
 
360
308
  const mod = await import('./index.js')
361
309
  expect(mod.PKG.externalDependencies).toContain('styled-components')
@@ -363,28 +311,35 @@ describe('tools-core', () => {
363
311
 
364
312
  it('should export VL_CONFIG as a function', async () => {
365
313
  vi.resetModules()
366
- setupDefaultMocks()
314
+ const dir = createTestDir({})
315
+ vi.spyOn(process, 'cwd').mockReturnValue(dir)
316
+
367
317
  const mod = await import('./index.js')
368
318
  expect(typeof mod.VL_CONFIG).toBe('function')
369
319
  })
370
320
 
371
321
  it('should export TS_CONFIG from tsconfig.json', async () => {
372
322
  vi.resetModules()
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') {
381
- return { compilerOptions: { strict: true } }
382
- }
383
- return null
323
+ const dir = createTestDir({
324
+ tsConfig: { compilerOptions: { strict: true } },
384
325
  })
326
+ vi.spyOn(process, 'cwd').mockReturnValue(dir)
385
327
 
386
328
  const mod = await import('./index.js')
387
329
  expect(mod.TS_CONFIG).toEqual({ compilerOptions: { strict: true } })
388
330
  })
331
+
332
+ it('should resolve VL_CONFIG from .mjs config at module level', async () => {
333
+ vi.resetModules()
334
+ const dir = createTestDir({
335
+ vlConfig: { stories: { framework: 'next', port: 3000 } },
336
+ })
337
+ vi.spyOn(process, 'cwd').mockReturnValue(dir)
338
+
339
+ const mod = await import('./index.js')
340
+ const stories = mod.VL_CONFIG('stories')
341
+ expect(stories.get('framework')).toBe('next')
342
+ expect(stories.get('port')).toBe(3000)
343
+ })
389
344
  })
390
345
  })
package/src/index.ts CHANGED
@@ -1,13 +1,11 @@
1
1
  import fs from 'node:fs'
2
- import { createRequire } from 'node:module'
3
2
  import path from 'node:path'
3
+ import { pathToFileURL } from 'node:url'
4
4
 
5
- const VL_CONFIG_FILES = ['vl-tools.config.mjs', 'vl-tools.config.js']
5
+ const VL_CONFIG_FILES = ['vl-tools.config.mjs']
6
6
  const PACKAGE_FILE_NAME = 'package.json'
7
7
  const TYPESCRIPT_FILE_NAME = 'tsconfig.json'
8
8
 
9
- const require = createRequire(import.meta.url)
10
-
11
9
  // --------------------------------------------------------
12
10
  // Utility helpers (replaces lodash-es and find-up)
13
11
  // --------------------------------------------------------
@@ -78,43 +76,35 @@ const findFileUp = (
78
76
  // --------------------------------------------------------
79
77
  const findFile = (filename: string) => findFileUp(filename)
80
78
 
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
- }
90
-
91
79
  const loadFileToJSON = (filename: string): Record<string, any> => {
92
80
  const file = findFile(filename)
93
-
94
81
  if (!file) return {}
95
82
 
96
- // try to read an exported module first
97
- const data = loadModule(file)
98
- if (data && Object.keys(data).length > 0) return data
99
-
100
- // try to read a plain json file like tsconfig.json
101
83
  try {
102
84
  return JSON.parse(fs.readFileSync(file, 'utf-8'))
103
85
  } catch (_e) {
104
- // ignore error
86
+ return {}
105
87
  }
88
+ }
106
89
 
107
- return {}
90
+ const loadModuleAsync = async (
91
+ filePath: string,
92
+ ): Promise<Record<string, any>> => {
93
+ try {
94
+ const mod = await import(pathToFileURL(filePath).href)
95
+ return mod?.default ?? mod ?? {}
96
+ } catch (e: any) {
97
+ console.warn(
98
+ `[tools-core] Failed to load config: ${filePath}\n ${e.message}`,
99
+ )
100
+ return {}
101
+ }
108
102
  }
109
103
 
110
104
  // --------------------------------------------------------
111
105
  // GET PACKAGE.JSON info
112
106
  // --------------------------------------------------------
113
- const getPackageJSON = () => {
114
- const data = loadFileToJSON(PACKAGE_FILE_NAME)
115
-
116
- return data
117
- }
107
+ const getPackageJSON = () => loadFileToJSON(PACKAGE_FILE_NAME)
118
108
 
119
109
  // --------------------------------------------------------
120
110
  // PACKAGE.json parsing functions
@@ -168,7 +158,7 @@ const getPkgData = (): Record<string, any> => {
168
158
 
169
159
  // --------------------------------------------------------
170
160
  // LOAD EXTERNAL CONFIGURATION
171
- // Cascading: finds all vl-tools.config.{mjs,js} files from
161
+ // Cascading: finds all vl-tools.config.mjs files from
172
162
  // cwd upward, then deep-merges them (root first, closest
173
163
  // package config wins).
174
164
  // --------------------------------------------------------
@@ -189,12 +179,12 @@ const findAllConfigFiles = (): string[] => {
189
179
  return files.reverse()
190
180
  }
191
181
 
192
- const getExternalConfig = (): Record<string, any> => {
182
+ const getExternalConfig = async (): Promise<Record<string, any>> => {
193
183
  const files = findAllConfigFiles()
194
184
  let config: Record<string, any> = {}
195
185
 
196
186
  for (const file of files) {
197
- const loaded = loadModule(file)
187
+ const loaded = await loadModuleAsync(file)
198
188
  config = deepMerge(config, loaded)
199
189
  }
200
190
 
@@ -209,8 +199,8 @@ const loadConfigParam =
209
199
  return get(externalConfig, key, defaultValue)
210
200
  }
211
201
 
212
- const loadVLToolsConfig = () => {
213
- const externalConfig = getExternalConfig()
202
+ const loadVLToolsConfig = async () => {
203
+ const externalConfig = await getExternalConfig()
214
204
 
215
205
  const cloneAndEnhance = (object: Record<string, any>) => ({
216
206
  get config() {
@@ -243,7 +233,7 @@ const swapGlobals = (globals: Record<string, string>) =>
243
233
  )
244
234
 
245
235
  const PKG = getPkgData()
246
- const VL_CONFIG = loadVLToolsConfig()
236
+ const VL_CONFIG = await loadVLToolsConfig()
247
237
  const TS_CONFIG = loadFileToJSON(TYPESCRIPT_FILE_NAME)
248
238
 
249
239
  export {