@graphcommerce/next-config 10.0.0-canary.68 β 10.0.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/CHANGELOG.md +126 -0
- package/__tests__/commands/copyFiles.ts +171 -135
- package/__tests__/config/utils/__snapshots__/mergeEnvIntoConfig.ts.snap +1 -1
- package/__tests__/interceptors/findPlugins.ts +0 -8
- package/__tests__/interceptors/generateInterceptors.ts +3 -2
- package/dist/config/loadConfig.js +2 -2
- package/dist/generated/config.js +0 -3
- package/dist/index.js +5 -4
- package/dist/{loadConfig-nJiCKeL1.js β loadConfig-CZYUe1jE.js} +1 -1
- package/package.json +6 -7
- package/src/config/utils/mergeEnvIntoConfig.ts +1 -1
- package/src/generated/config.ts +0 -24
- package/src/utils/resolveDependenciesSync.ts +2 -1
- package/src/withGraphCommerce.ts +0 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,131 @@
|
|
|
1
1
|
# Change Log
|
|
2
2
|
|
|
3
|
+
## 10.0.0
|
|
4
|
+
|
|
5
|
+
### Major Changes
|
|
6
|
+
|
|
7
|
+
- [#2546](https://github.com/graphcommerce-org/graphcommerce/pull/2546) [`ed9332a`](https://github.com/graphcommerce-org/graphcommerce/commit/ed9332a7f78966d932041d9a7725641edc92b28d) - ## GraphCommerce 10 - Turbopack Support
|
|
8
|
+
|
|
9
|
+
This major release brings full Turbopack compatibility, dramatically improving development speed.
|
|
10
|
+
|
|
11
|
+
### π Turbopack-Compatible Interceptor System
|
|
12
|
+
|
|
13
|
+
The entire plugin/interceptor system has been rewritten to work with Turbopack:
|
|
14
|
+
|
|
15
|
+
- **No more Webpack plugins** - Removed `InterceptorPlugin` webpack plugin entirely
|
|
16
|
+
- **File-based interception** - Original files are moved to `.original.tsx` and replaced with interceptor content
|
|
17
|
+
- **Direct imports** - Interceptors import from `.original` files instead of embedding source
|
|
18
|
+
- **New CLI commands**:
|
|
19
|
+
- `graphcommerce codegen-interceptors` - Generate interceptor files
|
|
20
|
+
- `graphcommerce cleanup-interceptors` - Reset interceptor system, restore original files
|
|
21
|
+
- **Stable file hashing** - Deterministic interceptor generation for better caching
|
|
22
|
+
|
|
23
|
+
### βοΈ Treeshakable Configuration System
|
|
24
|
+
|
|
25
|
+
Replaced Webpack `DefinePlugin`-based `import.meta.graphCommerce` with a new generated configuration system:
|
|
26
|
+
|
|
27
|
+
- **New `codegen-config-values` command** - Generates TypeScript files with precise typing
|
|
28
|
+
- **Schema-driven** - Dynamically introspects Zod schemas to determine all available properties
|
|
29
|
+
- **Fully treeshakable** - Unused config values are eliminated from the bundle
|
|
30
|
+
- **Type-safe** - Uses `Get<GraphCommerceConfig, 'path'>` for nested property access
|
|
31
|
+
- **Separate files for nested objects** - Optimal treeshaking for complex configurations
|
|
32
|
+
|
|
33
|
+
### π§ withGraphCommerce Changes
|
|
34
|
+
|
|
35
|
+
- **Removed** `InterceptorPlugin` - No longer needed with file-based interception
|
|
36
|
+
- **Removed** `DefinePlugin` for `import.meta.graphCommerce` - Replaced with generated config
|
|
37
|
+
- **Removed** `@mui/*` alias rewrites - No longer required
|
|
38
|
+
- **Added** Turbopack loader rules for `.yaml`, `.yml`, and `.po` files
|
|
39
|
+
- **Added** `serverExternalPackages` for all `@whatwg-node/*` packages
|
|
40
|
+
- **Added** `optimizePackageImports` for better bundle optimization
|
|
41
|
+
- **Added** `images.qualities: [52, 75]` for Next.js image optimization
|
|
42
|
+
|
|
43
|
+
### π¦ Lingui Configuration
|
|
44
|
+
|
|
45
|
+
- **Renamed** `lingui.config.js` β `lingui.config.ts` with TypeScript support
|
|
46
|
+
- **Updated** `@graphcommerce/lingui-next/config` to TypeScript with proper exports
|
|
47
|
+
- **Simplified** formatter options
|
|
48
|
+
|
|
49
|
+
### βοΈ React 19 & Next.js 16 Compatibility
|
|
50
|
+
|
|
51
|
+
- Updated `RefObject<T>` types for React 19 (now includes `null` by default)
|
|
52
|
+
- Replaced deprecated `React.VFC` with `React.FC`
|
|
53
|
+
- Fixed `useRef` calls to require explicit initial values
|
|
54
|
+
- Updated `MutableRefObject` usage in `framer-scroller`
|
|
55
|
+
|
|
56
|
+
### π ESLint 9 Flat Config
|
|
57
|
+
|
|
58
|
+
- Migrated from legacy `.eslintrc` to new flat config format (`eslint.config.mjs`)
|
|
59
|
+
- Updated `@typescript-eslint/*` packages to v8
|
|
60
|
+
- Fixed AST selector for `SxProps` rule (`typeParameters` β `typeArguments`)
|
|
61
|
+
|
|
62
|
+
### π Apollo Client
|
|
63
|
+
|
|
64
|
+
- Fixed deprecated `name` option β `clientAwareness: { name: 'ssr' }`
|
|
65
|
+
- Updated error handling types to accept `ApolloError | null | undefined`
|
|
66
|
+
|
|
67
|
+
### β οΈ Breaking Changes
|
|
68
|
+
|
|
69
|
+
- **Node.js 24.x not supported** - Restricted to `>=20 <24.0.0` due to [nodejs/undici#4290](https://github.com/nodejs/undici/issues/4290)
|
|
70
|
+
- **Interceptor files changed** - Original components now at `.original.tsx`
|
|
71
|
+
- **Config access changed** - Use generated config values instead of `import.meta.graphCommerce`
|
|
72
|
+
- **ESLint config format** - Must use flat config (`eslint.config.mjs`)
|
|
73
|
+
- **Lingui config** - Rename `lingui.config.js` to `lingui.config.ts`
|
|
74
|
+
|
|
75
|
+
### ποΈ Removed
|
|
76
|
+
|
|
77
|
+
- `InterceptorPlugin` webpack plugin
|
|
78
|
+
- `configToImportMeta` utility
|
|
79
|
+
- Webpack `DefinePlugin` usage for config
|
|
80
|
+
- `@mui/*` modern alias rewrites
|
|
81
|
+
- Debug plugins (`CircularDependencyPlugin`, `DuplicatesPlugin`) ([@paales](https://github.com/paales))
|
|
82
|
+
|
|
83
|
+
- [#2565](https://github.com/graphcommerce-org/graphcommerce/pull/2565) [`c96dfcd`](https://github.com/graphcommerce-org/graphcommerce/commit/c96dfcdca981baca387c270ad9e2b9515cdd00cc) - Updated to Apollo Client 4 ([@paales](https://github.com/paales))
|
|
84
|
+
|
|
85
|
+
### Minor Changes
|
|
86
|
+
|
|
87
|
+
- [#2565](https://github.com/graphcommerce-org/graphcommerce/pull/2565) [`ea75841`](https://github.com/graphcommerce-org/graphcommerce/commit/ea758413b1ee8b8f2584e8f4135cb582fb87445f) - Migrated to vitest ([@paales](https://github.com/paales))
|
|
88
|
+
|
|
89
|
+
### Patch Changes
|
|
90
|
+
|
|
91
|
+
- [#2487](https://github.com/graphcommerce-org/graphcommerce/pull/2487) [`8b25322`](https://github.com/graphcommerce-org/graphcommerce/commit/8b253224997b59ac74d72813214dfc224f526c0a) - When a dependency is optional or has peerDependenciesMeta set to optional, make sure it doesn't crash when it is not found when calling resolveDependenciesSync ([@paales](https://github.com/paales))
|
|
92
|
+
|
|
93
|
+
- [#2487](https://github.com/graphcommerce-org/graphcommerce/pull/2487) [`2c79a4c`](https://github.com/graphcommerce-org/graphcommerce/commit/2c79a4cba2779bc367104ebb13e6c0d6feb6574f) - Remove redirects for `/product/$type/[url]` routes, those haven't been used for years anymore. ([@paales](https://github.com/paales))
|
|
94
|
+
|
|
95
|
+
- [#2472](https://github.com/graphcommerce-org/graphcommerce/pull/2472) [`905157b`](https://github.com/graphcommerce-org/graphcommerce/commit/905157bec2c9e1dcf51b6e6f7b6913aa9be7b609) - Solve issue with chalk compilation because weβre not migrated to esm modules. ([@paales](https://github.com/paales))
|
|
96
|
+
|
|
97
|
+
- [`f6b9c0d`](https://github.com/graphcommerce-org/graphcommerce/commit/f6b9c0d2bc5f678fc6cde279f1590b30ef631f8a) - Temporarily pin prettier to 3.5.3 to fix the GraphCommerce build: https://github.com/hosseinmd/prettier-plugin-jsdoc/pull/246/files ([@paales](https://github.com/paales))
|
|
98
|
+
|
|
99
|
+
- [#2487](https://github.com/graphcommerce-org/graphcommerce/pull/2487) [`30b7356`](https://github.com/graphcommerce-org/graphcommerce/commit/30b7356790efbac0f0017ef61cb1619b920100ab) - Solve issue where withGraphCommerce had a hard dependency on Magento specific configurations ([@paales](https://github.com/paales))
|
|
100
|
+
|
|
101
|
+
- [`62b71d5`](https://github.com/graphcommerce-org/graphcommerce/commit/62b71d5ed1d482fdb5b76dc19ac4d55fc41fb191) - Solve issue: TypeError: InMemoryLRUCache is not a constructor ([@paales](https://github.com/paales))
|
|
102
|
+
|
|
103
|
+
- [#2537](https://github.com/graphcommerce-org/graphcommerce/pull/2537) [`6492d81`](https://github.com/graphcommerce-org/graphcommerce/commit/6492d819a858ffdc01a97e6914053e9cc14c8fc5) - Reduce exported scope of config so we dont introduce a hidden dependency on Magento ([@paales](https://github.com/paales))
|
|
104
|
+
|
|
105
|
+
- [`f508396`](https://github.com/graphcommerce-org/graphcommerce/commit/f50839619c75c8f7b1782c079f1c25998cb9217b) - Solve issue where optional chaining for import.meta.graphCommmerce configuration paths didn't work. ([@paales](https://github.com/paales))
|
|
106
|
+
|
|
107
|
+
- [#2499](https://github.com/graphcommerce-org/graphcommerce/pull/2499) [`9cf0450`](https://github.com/graphcommerce-org/graphcommerce/commit/9cf0450a41c4c74292b96f5795c7428f9d6a930c) - Remove the 'ignored' string when loading the graphcommerce config env variable loading, as that isn't always correct ([@paales](https://github.com/paales))
|
|
108
|
+
|
|
109
|
+
- [#2487](https://github.com/graphcommerce-org/graphcommerce/pull/2487) [`9825f59`](https://github.com/graphcommerce-org/graphcommerce/commit/9825f59b8626c315e6092950faceeab4311a5424) - Remove rewriteLegacyEnv as that hasn't been used for years ([@paales](https://github.com/paales))
|
|
110
|
+
|
|
111
|
+
- [#2487](https://github.com/graphcommerce-org/graphcommerce/pull/2487) [`5ffa0ee`](https://github.com/graphcommerce-org/graphcommerce/commit/5ffa0ee1d620f4f1c38bc5df36cbd527bcc43cf9) - Migrated `@graphcommerce/next-config` package to `"type": "module"` ([@paales](https://github.com/paales))
|
|
112
|
+
|
|
113
|
+
## 10.0.0-canary.72
|
|
114
|
+
|
|
115
|
+
## 10.0.0-canary.71
|
|
116
|
+
|
|
117
|
+
## 10.0.0-canary.70
|
|
118
|
+
|
|
119
|
+
### Major Changes
|
|
120
|
+
|
|
121
|
+
- [#2565](https://github.com/graphcommerce-org/graphcommerce/pull/2565) [`c96dfcd`](https://github.com/graphcommerce-org/graphcommerce/commit/c96dfcdca981baca387c270ad9e2b9515cdd00cc) - Updated to Apollo Client 4 ([@paales](https://github.com/paales))
|
|
122
|
+
|
|
123
|
+
### Minor Changes
|
|
124
|
+
|
|
125
|
+
- [#2565](https://github.com/graphcommerce-org/graphcommerce/pull/2565) [`ea75841`](https://github.com/graphcommerce-org/graphcommerce/commit/ea758413b1ee8b8f2584e8f4135cb582fb87445f) - Migrated to vitest ([@paales](https://github.com/paales))
|
|
126
|
+
|
|
127
|
+
## 10.0.0-canary.69
|
|
128
|
+
|
|
3
129
|
## 10.0.0-canary.68
|
|
4
130
|
|
|
5
131
|
## 10.0.0-canary.67
|
|
@@ -1,32 +1,49 @@
|
|
|
1
|
+
// Import after mocks are defined
|
|
1
2
|
import fs from 'fs/promises'
|
|
2
3
|
import path from 'path'
|
|
4
|
+
import fg from 'fast-glob'
|
|
5
|
+
import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it, vi } from 'vitest'
|
|
3
6
|
import { copyFiles } from '../../src/commands/copyFiles'
|
|
4
7
|
import { resolveDependenciesSync } from '../../src/utils/resolveDependenciesSync'
|
|
5
8
|
|
|
6
9
|
// Mock fs/promises
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
10
|
+
vi.mock('fs/promises', () => ({
|
|
11
|
+
default: {
|
|
12
|
+
readFile: vi.fn(),
|
|
13
|
+
writeFile: vi.fn(),
|
|
14
|
+
mkdir: vi.fn(),
|
|
15
|
+
readdir: vi.fn(),
|
|
16
|
+
stat: vi.fn(),
|
|
17
|
+
unlink: vi.fn(),
|
|
18
|
+
rmdir: vi.fn(),
|
|
19
|
+
},
|
|
20
|
+
readFile: vi.fn(),
|
|
21
|
+
writeFile: vi.fn(),
|
|
22
|
+
mkdir: vi.fn(),
|
|
23
|
+
readdir: vi.fn(),
|
|
24
|
+
stat: vi.fn(),
|
|
25
|
+
unlink: vi.fn(),
|
|
26
|
+
rmdir: vi.fn(),
|
|
15
27
|
}))
|
|
16
28
|
|
|
17
29
|
// Mock fast-glob
|
|
18
|
-
|
|
30
|
+
vi.mock('fast-glob', () => ({
|
|
19
31
|
__esModule: true,
|
|
20
|
-
default:
|
|
32
|
+
default: vi.fn(),
|
|
21
33
|
}))
|
|
22
34
|
|
|
23
35
|
// Mock resolveDependenciesSync
|
|
24
|
-
|
|
25
|
-
resolveDependenciesSync:
|
|
36
|
+
vi.mock('../../src/utils/resolveDependenciesSync', () => ({
|
|
37
|
+
resolveDependenciesSync: vi.fn(),
|
|
26
38
|
}))
|
|
27
39
|
|
|
40
|
+
// Get mocked versions
|
|
41
|
+
const mockFs = vi.mocked(fs)
|
|
42
|
+
const mockFg = vi.mocked(fg)
|
|
43
|
+
const mockResolveDependenciesSync = vi.mocked(resolveDependenciesSync)
|
|
44
|
+
|
|
28
45
|
// Mock performance.now
|
|
29
|
-
const mockPerformanceNow =
|
|
46
|
+
const mockPerformanceNow = vi.fn()
|
|
30
47
|
global.performance = { now: mockPerformanceNow } as unknown as typeof performance
|
|
31
48
|
|
|
32
49
|
// Mock process.cwd
|
|
@@ -34,7 +51,7 @@ const mockCwd = '/mock/cwd'
|
|
|
34
51
|
// eslint-disable-next-line @typescript-eslint/unbound-method
|
|
35
52
|
const originalCwd = process.cwd
|
|
36
53
|
beforeAll(() => {
|
|
37
|
-
process.cwd =
|
|
54
|
+
process.cwd = vi.fn().mockReturnValue(mockCwd)
|
|
38
55
|
})
|
|
39
56
|
|
|
40
57
|
afterAll(() => {
|
|
@@ -42,31 +59,31 @@ afterAll(() => {
|
|
|
42
59
|
})
|
|
43
60
|
|
|
44
61
|
describe('copyFiles', () => {
|
|
45
|
-
let consoleLog:
|
|
46
|
-
let consoleInfo:
|
|
47
|
-
let consoleError:
|
|
48
|
-
let processExit:
|
|
62
|
+
let consoleLog: ReturnType<typeof vi.spyOn>
|
|
63
|
+
let consoleInfo: ReturnType<typeof vi.spyOn>
|
|
64
|
+
let consoleError: ReturnType<typeof vi.spyOn>
|
|
65
|
+
let processExit: ReturnType<typeof vi.spyOn>
|
|
49
66
|
let originalDebug: string | undefined
|
|
50
67
|
|
|
51
|
-
beforeEach(() => {
|
|
52
|
-
|
|
68
|
+
beforeEach(async () => {
|
|
69
|
+
vi.clearAllMocks()
|
|
53
70
|
originalDebug = process.env.DEBUG
|
|
54
71
|
process.env.DEBUG = undefined
|
|
55
|
-
|
|
72
|
+
mockResolveDependenciesSync.mockReturnValue(
|
|
56
73
|
new Map([
|
|
57
74
|
['@graphcommerce/package1', 'packages/package1'],
|
|
58
75
|
['@graphcommerce/package2', 'packages/package2'],
|
|
59
76
|
]),
|
|
60
77
|
)
|
|
61
|
-
consoleLog =
|
|
62
|
-
consoleInfo =
|
|
63
|
-
consoleError =
|
|
64
|
-
processExit =
|
|
78
|
+
consoleLog = vi.spyOn(console, 'log').mockImplementation(() => {})
|
|
79
|
+
consoleInfo = vi.spyOn(console, 'info').mockImplementation(() => {})
|
|
80
|
+
consoleError = vi.spyOn(console, 'error').mockImplementation(() => {})
|
|
81
|
+
processExit = vi.spyOn(process, 'exit').mockImplementation(() => undefined as never)
|
|
65
82
|
|
|
66
83
|
// Setup default .gitignore mock
|
|
67
|
-
|
|
84
|
+
mockFs.readFile.mockImplementation((filePath) => {
|
|
68
85
|
if (filePath === path.join(mockCwd, '.gitignore')) {
|
|
69
|
-
return Promise.resolve('existing\ngitignore\ncontent')
|
|
86
|
+
return Promise.resolve('existing\ngitignore\ncontent') as ReturnType<typeof fs.readFile>
|
|
70
87
|
}
|
|
71
88
|
return Promise.reject(new Error(`ENOENT: no such file or directory, open '${filePath}'`))
|
|
72
89
|
})
|
|
@@ -87,12 +104,11 @@ describe('copyFiles', () => {
|
|
|
87
104
|
})
|
|
88
105
|
|
|
89
106
|
it('should handle empty source directories', async () => {
|
|
90
|
-
|
|
91
|
-
fg.mockResolvedValue([])
|
|
107
|
+
mockFg.mockResolvedValue([])
|
|
92
108
|
|
|
93
109
|
await copyFiles()
|
|
94
110
|
|
|
95
|
-
expect(
|
|
111
|
+
expect(mockFg).toHaveBeenCalledWith('**/*', {
|
|
96
112
|
cwd: mockCwd,
|
|
97
113
|
dot: true,
|
|
98
114
|
ignore: ['**/dist/**', '**/build/**', '**/.next/**', '**/.git/**', '**/node_modules/**'],
|
|
@@ -101,48 +117,49 @@ describe('copyFiles', () => {
|
|
|
101
117
|
})
|
|
102
118
|
|
|
103
119
|
it('should copy files and add management comments', async () => {
|
|
104
|
-
|
|
105
|
-
|
|
120
|
+
mockFg
|
|
121
|
+
.mockResolvedValueOnce([]) // First scan for existing files
|
|
106
122
|
.mockResolvedValueOnce(['file.ts']) // Second scan for package files
|
|
107
|
-
|
|
123
|
+
mockFs.readFile.mockImplementation((filePath) => {
|
|
108
124
|
if (filePath === path.join('packages/package1/copy', 'file.ts')) {
|
|
109
|
-
return Promise.resolve(Buffer.from('content'))
|
|
125
|
+
return Promise.resolve(Buffer.from('content')) as ReturnType<typeof fs.readFile>
|
|
110
126
|
}
|
|
111
127
|
if (filePath === path.join(mockCwd, '.gitignore')) {
|
|
112
|
-
return Promise.resolve('existing\ngitignore\ncontent')
|
|
128
|
+
return Promise.resolve('existing\ngitignore\ncontent') as ReturnType<typeof fs.readFile>
|
|
113
129
|
}
|
|
114
130
|
return Promise.reject(new Error(`ENOENT: no such file or directory, open '${filePath}'`))
|
|
115
131
|
})
|
|
116
132
|
|
|
117
|
-
|
|
118
|
-
|
|
133
|
+
mockFs.stat.mockResolvedValue({ isDirectory: () => false } as Awaited<
|
|
134
|
+
ReturnType<typeof fs.stat>
|
|
135
|
+
>)
|
|
119
136
|
|
|
120
137
|
await copyFiles()
|
|
121
138
|
|
|
122
139
|
// Verify file was written with management comments
|
|
123
|
-
const writeCall =
|
|
140
|
+
const writeCall = mockFs.writeFile.mock.calls.find(
|
|
124
141
|
(call) => call[0] === path.join(mockCwd, 'file.ts'),
|
|
125
142
|
)
|
|
126
143
|
expect(writeCall).toBeTruthy()
|
|
127
|
-
const content = writeCall[1].toString()
|
|
144
|
+
const content = writeCall![1].toString()
|
|
128
145
|
expect(content).toContain('// managed by: graphcommerce')
|
|
129
146
|
expect(content).toContain('// to modify this file, change it to managed by: local')
|
|
130
147
|
expect(content).toContain('content')
|
|
131
148
|
|
|
132
149
|
// Verify .gitignore was updated
|
|
133
|
-
const gitignoreCall =
|
|
150
|
+
const gitignoreCall = mockFs.writeFile.mock.calls.find(
|
|
134
151
|
(call) => call[0] === path.join(mockCwd, '.gitignore'),
|
|
135
152
|
)
|
|
136
153
|
expect(gitignoreCall).toBeTruthy()
|
|
137
|
-
const gitignoreContent = gitignoreCall[1].toString()
|
|
154
|
+
const gitignoreContent = gitignoreCall![1].toString()
|
|
138
155
|
expect(gitignoreContent).toContain('# managed by: graphcommerce')
|
|
139
156
|
expect(gitignoreContent).toContain('file.ts')
|
|
140
157
|
expect(gitignoreContent).toContain('# end managed by: graphcommerce')
|
|
141
158
|
})
|
|
142
159
|
|
|
143
160
|
it('should handle existing managed files with identical content', async () => {
|
|
144
|
-
|
|
145
|
-
|
|
161
|
+
mockFg
|
|
162
|
+
.mockResolvedValueOnce([]) // First scan for existing files
|
|
146
163
|
.mockResolvedValueOnce(['file.ts']) // Second scan for package files
|
|
147
164
|
|
|
148
165
|
const sourceContent = Buffer.from('content')
|
|
@@ -153,15 +170,15 @@ describe('copyFiles', () => {
|
|
|
153
170
|
sourceContent,
|
|
154
171
|
])
|
|
155
172
|
|
|
156
|
-
|
|
173
|
+
mockFs.readFile.mockImplementation((filePath) => {
|
|
157
174
|
if (filePath === path.join('packages/package1/copy', 'file.ts')) {
|
|
158
|
-
return Promise.resolve(sourceContent)
|
|
175
|
+
return Promise.resolve(sourceContent) as ReturnType<typeof fs.readFile>
|
|
159
176
|
}
|
|
160
177
|
if (filePath === path.join(mockCwd, 'file.ts')) {
|
|
161
|
-
return Promise.resolve(managedContent)
|
|
178
|
+
return Promise.resolve(managedContent) as ReturnType<typeof fs.readFile>
|
|
162
179
|
}
|
|
163
180
|
if (filePath === path.join(mockCwd, '.gitignore')) {
|
|
164
|
-
return Promise.resolve('existing\ngitignore\ncontent')
|
|
181
|
+
return Promise.resolve('existing\ngitignore\ncontent') as ReturnType<typeof fs.readFile>
|
|
165
182
|
}
|
|
166
183
|
return Promise.reject(new Error(`ENOENT: no such file or directory, open '${filePath}'`))
|
|
167
184
|
})
|
|
@@ -169,13 +186,16 @@ describe('copyFiles', () => {
|
|
|
169
186
|
await copyFiles()
|
|
170
187
|
|
|
171
188
|
// Should not write to the file since content is identical
|
|
172
|
-
expect(
|
|
173
|
-
expect(
|
|
189
|
+
expect(mockFs.writeFile).toHaveBeenCalledTimes(1) // Only .gitignore should be written
|
|
190
|
+
expect(mockFs.writeFile).toHaveBeenCalledWith(
|
|
191
|
+
path.join(mockCwd, '.gitignore'),
|
|
192
|
+
expect.any(String),
|
|
193
|
+
)
|
|
174
194
|
})
|
|
175
195
|
|
|
176
196
|
it('should update existing managed files with different content', async () => {
|
|
177
|
-
|
|
178
|
-
|
|
197
|
+
mockFg
|
|
198
|
+
.mockResolvedValueOnce([]) // First scan for existing files
|
|
179
199
|
.mockResolvedValueOnce(['file.ts']) // Second scan for package files
|
|
180
200
|
|
|
181
201
|
const sourceContent = Buffer.from('new content')
|
|
@@ -186,15 +206,15 @@ describe('copyFiles', () => {
|
|
|
186
206
|
Buffer.from('old content'),
|
|
187
207
|
])
|
|
188
208
|
|
|
189
|
-
|
|
209
|
+
mockFs.readFile.mockImplementation((filePath) => {
|
|
190
210
|
if (filePath === path.join('packages/package1/copy', 'file.ts')) {
|
|
191
|
-
return Promise.resolve(sourceContent)
|
|
211
|
+
return Promise.resolve(sourceContent) as ReturnType<typeof fs.readFile>
|
|
192
212
|
}
|
|
193
213
|
if (filePath === path.join(mockCwd, 'file.ts')) {
|
|
194
|
-
return Promise.resolve(oldContent)
|
|
214
|
+
return Promise.resolve(oldContent) as ReturnType<typeof fs.readFile>
|
|
195
215
|
}
|
|
196
216
|
if (filePath === path.join(mockCwd, '.gitignore')) {
|
|
197
|
-
return Promise.resolve('existing\ngitignore\ncontent')
|
|
217
|
+
return Promise.resolve('existing\ngitignore\ncontent') as ReturnType<typeof fs.readFile>
|
|
198
218
|
}
|
|
199
219
|
return Promise.reject(new Error(`ENOENT: no such file or directory, open '${filePath}'`))
|
|
200
220
|
})
|
|
@@ -202,25 +222,25 @@ describe('copyFiles', () => {
|
|
|
202
222
|
await copyFiles()
|
|
203
223
|
|
|
204
224
|
// Should write the new content
|
|
205
|
-
const writeCall =
|
|
225
|
+
const writeCall = mockFs.writeFile.mock.calls.find(
|
|
206
226
|
(call) => call[0] === path.join(mockCwd, 'file.ts'),
|
|
207
227
|
)
|
|
208
228
|
expect(writeCall).toBeTruthy()
|
|
209
|
-
const content = writeCall[1].toString()
|
|
229
|
+
const content = writeCall![1].toString()
|
|
210
230
|
expect(content).toContain('new content')
|
|
211
231
|
expect(consoleInfo).toHaveBeenCalledWith('Updated managed file: file.ts')
|
|
212
232
|
})
|
|
213
233
|
|
|
214
234
|
it('should create new files with management comments', async () => {
|
|
215
|
-
|
|
216
|
-
|
|
235
|
+
mockFg
|
|
236
|
+
.mockResolvedValueOnce([]) // First scan for existing files
|
|
217
237
|
.mockResolvedValueOnce(['new-file.ts']) // Second scan for package files
|
|
218
|
-
|
|
238
|
+
mockFs.readFile.mockImplementation((filePath) => {
|
|
219
239
|
if (filePath === path.join('packages/package1/copy', 'new-file.ts')) {
|
|
220
|
-
return Promise.resolve(Buffer.from('content'))
|
|
240
|
+
return Promise.resolve(Buffer.from('content')) as ReturnType<typeof fs.readFile>
|
|
221
241
|
}
|
|
222
242
|
if (filePath === path.join(mockCwd, '.gitignore')) {
|
|
223
|
-
return Promise.resolve('existing\ngitignore\ncontent')
|
|
243
|
+
return Promise.resolve('existing\ngitignore\ncontent') as ReturnType<typeof fs.readFile>
|
|
224
244
|
}
|
|
225
245
|
if (filePath === path.join(mockCwd, 'new-file.ts')) {
|
|
226
246
|
return Promise.reject(new Error('ENOENT: no such file or directory'))
|
|
@@ -233,22 +253,27 @@ describe('copyFiles', () => {
|
|
|
233
253
|
expect(consoleInfo).toHaveBeenCalledWith(
|
|
234
254
|
'Creating new file: new-file.ts\nSource: packages/package1/copy/new-file.ts',
|
|
235
255
|
)
|
|
236
|
-
expect(
|
|
256
|
+
expect(mockFs.writeFile).toHaveBeenCalledWith(
|
|
257
|
+
path.join(mockCwd, 'new-file.ts'),
|
|
258
|
+
expect.any(Buffer),
|
|
259
|
+
)
|
|
237
260
|
})
|
|
238
261
|
|
|
239
262
|
it('should handle locally managed files', async () => {
|
|
240
|
-
|
|
241
|
-
|
|
263
|
+
mockFg
|
|
264
|
+
.mockResolvedValueOnce([]) // First scan for existing files
|
|
242
265
|
.mockResolvedValueOnce(['file.ts']) // Second scan for package files
|
|
243
|
-
|
|
266
|
+
mockFs.readFile.mockImplementation((filePath) => {
|
|
244
267
|
if (filePath === path.join('packages/package1/copy', 'file.ts')) {
|
|
245
|
-
return Promise.resolve(Buffer.from('content'))
|
|
268
|
+
return Promise.resolve(Buffer.from('content')) as ReturnType<typeof fs.readFile>
|
|
246
269
|
}
|
|
247
270
|
if (filePath === path.join(mockCwd, 'file.ts')) {
|
|
248
|
-
return Promise.resolve(Buffer.from('// managed by: local\ncontent'))
|
|
271
|
+
return Promise.resolve(Buffer.from('// managed by: local\ncontent')) as ReturnType<
|
|
272
|
+
typeof fs.readFile
|
|
273
|
+
>
|
|
249
274
|
}
|
|
250
275
|
if (filePath === path.join(mockCwd, '.gitignore')) {
|
|
251
|
-
return Promise.resolve('existing\ngitignore\ncontent')
|
|
276
|
+
return Promise.resolve('existing\ngitignore\ncontent') as ReturnType<typeof fs.readFile>
|
|
252
277
|
}
|
|
253
278
|
return Promise.reject(new Error(`ENOENT: no such file or directory, open '${filePath}'`))
|
|
254
279
|
})
|
|
@@ -256,42 +281,44 @@ describe('copyFiles', () => {
|
|
|
256
281
|
await copyFiles()
|
|
257
282
|
|
|
258
283
|
// Should not overwrite locally managed files
|
|
259
|
-
expect(
|
|
260
|
-
expect(
|
|
284
|
+
expect(mockFs.writeFile).toHaveBeenCalledTimes(1) // Only .gitignore should be written
|
|
285
|
+
expect(mockFs.writeFile).toHaveBeenCalledWith(
|
|
286
|
+
path.join(mockCwd, '.gitignore'),
|
|
287
|
+
expect.any(String),
|
|
288
|
+
)
|
|
261
289
|
})
|
|
262
290
|
|
|
263
291
|
it('should create destination directory if it does not exist', async () => {
|
|
264
|
-
|
|
265
|
-
|
|
292
|
+
mockFg
|
|
293
|
+
.mockResolvedValueOnce([]) // First scan for existing files
|
|
266
294
|
.mockResolvedValueOnce(['nested/file.ts']) // Second scan for package files
|
|
267
|
-
|
|
295
|
+
mockFs.readFile.mockImplementation((filePath) => {
|
|
268
296
|
if (filePath === path.join('packages/package1/copy', 'nested/file.ts')) {
|
|
269
|
-
return Promise.resolve(Buffer.from('content'))
|
|
297
|
+
return Promise.resolve(Buffer.from('content')) as ReturnType<typeof fs.readFile>
|
|
270
298
|
}
|
|
271
299
|
if (filePath === path.join(mockCwd, '.gitignore')) {
|
|
272
|
-
return Promise.resolve('existing\ngitignore\ncontent')
|
|
300
|
+
return Promise.resolve('existing\ngitignore\ncontent') as ReturnType<typeof fs.readFile>
|
|
273
301
|
}
|
|
274
302
|
return Promise.reject(new Error(`ENOENT: no such file or directory, open '${filePath}'`))
|
|
275
303
|
})
|
|
276
304
|
|
|
277
|
-
|
|
278
|
-
mockMkdir.mockResolvedValue(undefined)
|
|
305
|
+
mockFs.mkdir.mockResolvedValue(undefined)
|
|
279
306
|
|
|
280
307
|
await copyFiles()
|
|
281
308
|
|
|
282
|
-
expect(
|
|
309
|
+
expect(mockFs.mkdir).toHaveBeenCalledWith(path.join(mockCwd, 'nested'), { recursive: true })
|
|
283
310
|
})
|
|
284
311
|
|
|
285
312
|
it('should handle errors gracefully', async () => {
|
|
286
|
-
|
|
287
|
-
|
|
313
|
+
mockFg
|
|
314
|
+
.mockResolvedValueOnce([]) // First scan for existing files
|
|
288
315
|
.mockResolvedValueOnce(['file.ts']) // Second scan for package files
|
|
289
|
-
|
|
316
|
+
mockFs.readFile.mockImplementation((filePath) => {
|
|
290
317
|
if (filePath === path.join('packages/package1/copy', 'file.ts')) {
|
|
291
318
|
return Promise.reject(new Error('Read error'))
|
|
292
319
|
}
|
|
293
320
|
if (filePath === path.join(mockCwd, '.gitignore')) {
|
|
294
|
-
return Promise.resolve('existing\ngitignore\ncontent')
|
|
321
|
+
return Promise.resolve('existing\ngitignore\ncontent') as ReturnType<typeof fs.readFile>
|
|
295
322
|
}
|
|
296
323
|
return Promise.reject(new Error(`ENOENT: no such file or directory, open '${filePath}'`))
|
|
297
324
|
})
|
|
@@ -307,8 +334,8 @@ describe('copyFiles', () => {
|
|
|
307
334
|
})
|
|
308
335
|
|
|
309
336
|
it('should detect file conflicts between packages', async () => {
|
|
310
|
-
|
|
311
|
-
|
|
337
|
+
mockFg
|
|
338
|
+
.mockResolvedValueOnce([]) // First scan for existing files
|
|
312
339
|
.mockResolvedValueOnce(['conflict.ts']) // Package 1 files
|
|
313
340
|
.mockResolvedValueOnce(['conflict.ts']) // Package 2 files
|
|
314
341
|
|
|
@@ -321,40 +348,41 @@ describe('copyFiles', () => {
|
|
|
321
348
|
})
|
|
322
349
|
|
|
323
350
|
it('should remove files that are no longer provided', async () => {
|
|
324
|
-
|
|
325
|
-
|
|
351
|
+
mockFg
|
|
352
|
+
.mockResolvedValueOnce(['old-file.ts']) // First scan finds existing managed file
|
|
326
353
|
.mockResolvedValueOnce([]) // Second scan finds no files in packages
|
|
327
354
|
|
|
328
355
|
// Mock existing managed file
|
|
329
|
-
|
|
356
|
+
mockFs.readFile.mockImplementation((filePath) => {
|
|
330
357
|
if (filePath === path.join(mockCwd, 'old-file.ts')) {
|
|
331
|
-
return Promise.resolve(Buffer.from('// managed by: graphcommerce\ncontent'))
|
|
358
|
+
return Promise.resolve(Buffer.from('// managed by: graphcommerce\ncontent')) as ReturnType<
|
|
359
|
+
typeof fs.readFile
|
|
360
|
+
>
|
|
332
361
|
}
|
|
333
362
|
if (filePath === path.join(mockCwd, '.gitignore')) {
|
|
334
|
-
return Promise.resolve('existing\ngitignore\ncontent')
|
|
363
|
+
return Promise.resolve('existing\ngitignore\ncontent') as ReturnType<typeof fs.readFile>
|
|
335
364
|
}
|
|
336
365
|
return Promise.reject(new Error(`ENOENT: no such file or directory, open '${filePath}'`))
|
|
337
366
|
})
|
|
338
367
|
|
|
339
368
|
// Mock directory checks
|
|
340
|
-
|
|
341
|
-
mockReaddir.mockImplementation((dirPath: string) => {
|
|
369
|
+
mockFs.readdir.mockImplementation((dirPath) => {
|
|
342
370
|
if (dirPath === mockCwd) {
|
|
343
|
-
return Promise.resolve(['old-file.ts'])
|
|
371
|
+
return Promise.resolve(['old-file.ts']) as ReturnType<typeof fs.readdir>
|
|
344
372
|
}
|
|
345
|
-
return Promise.resolve([])
|
|
373
|
+
return Promise.resolve([]) as ReturnType<typeof fs.readdir>
|
|
346
374
|
})
|
|
347
375
|
|
|
348
376
|
await copyFiles()
|
|
349
377
|
|
|
350
378
|
// Verify file was removed
|
|
351
|
-
expect(
|
|
379
|
+
expect(mockFs.unlink).toHaveBeenCalledWith(path.join(mockCwd, 'old-file.ts'))
|
|
352
380
|
|
|
353
381
|
// Verify directory was checked
|
|
354
|
-
expect(
|
|
382
|
+
expect(mockFs.readdir).toHaveBeenCalledWith(mockCwd)
|
|
355
383
|
|
|
356
384
|
// Verify .gitignore was updated
|
|
357
|
-
expect(
|
|
385
|
+
expect(mockFs.writeFile).toHaveBeenCalledWith(
|
|
358
386
|
path.join(mockCwd, '.gitignore'),
|
|
359
387
|
expect.stringContaining('existing\ngitignore\ncontent'),
|
|
360
388
|
)
|
|
@@ -362,15 +390,15 @@ describe('copyFiles', () => {
|
|
|
362
390
|
|
|
363
391
|
it('should handle debug mode', async () => {
|
|
364
392
|
process.env.DEBUG = 'true'
|
|
365
|
-
|
|
366
|
-
|
|
393
|
+
mockFg
|
|
394
|
+
.mockResolvedValueOnce([]) // First scan for existing files
|
|
367
395
|
.mockResolvedValueOnce(['file.ts']) // Second scan for package files
|
|
368
|
-
|
|
396
|
+
mockFs.readFile.mockImplementation((filePath) => {
|
|
369
397
|
if (filePath === path.join('packages/package1/copy', 'file.ts')) {
|
|
370
|
-
return Promise.resolve(Buffer.from('content'))
|
|
398
|
+
return Promise.resolve(Buffer.from('content')) as ReturnType<typeof fs.readFile>
|
|
371
399
|
}
|
|
372
400
|
if (filePath === path.join(mockCwd, '.gitignore')) {
|
|
373
|
-
return Promise.resolve('existing\ngitignore\ncontent')
|
|
401
|
+
return Promise.resolve('existing\ngitignore\ncontent') as ReturnType<typeof fs.readFile>
|
|
374
402
|
}
|
|
375
403
|
return Promise.reject(new Error(`ENOENT: no such file or directory, open '${filePath}'`))
|
|
376
404
|
})
|
|
@@ -382,18 +410,18 @@ describe('copyFiles', () => {
|
|
|
382
410
|
})
|
|
383
411
|
|
|
384
412
|
it('should handle unmanaged files', async () => {
|
|
385
|
-
|
|
386
|
-
|
|
413
|
+
mockFg
|
|
414
|
+
.mockResolvedValueOnce([]) // First scan for existing files
|
|
387
415
|
.mockResolvedValueOnce(['file.ts']) // Second scan for package files
|
|
388
|
-
|
|
416
|
+
mockFs.readFile.mockImplementation((filePath) => {
|
|
389
417
|
if (filePath === path.join('packages/package1/copy', 'file.ts')) {
|
|
390
|
-
return Promise.resolve(Buffer.from('content'))
|
|
418
|
+
return Promise.resolve(Buffer.from('content')) as ReturnType<typeof fs.readFile>
|
|
391
419
|
}
|
|
392
420
|
if (filePath === path.join(mockCwd, 'file.ts')) {
|
|
393
|
-
return Promise.resolve(Buffer.from('unmanaged content'))
|
|
421
|
+
return Promise.resolve(Buffer.from('unmanaged content')) as ReturnType<typeof fs.readFile>
|
|
394
422
|
}
|
|
395
423
|
if (filePath === path.join(mockCwd, '.gitignore')) {
|
|
396
|
-
return Promise.resolve('existing\ngitignore\ncontent')
|
|
424
|
+
return Promise.resolve('existing\ngitignore\ncontent') as ReturnType<typeof fs.readFile>
|
|
397
425
|
}
|
|
398
426
|
return Promise.reject(new Error(`ENOENT: no such file or directory, open '${filePath}'`))
|
|
399
427
|
})
|
|
@@ -406,32 +434,34 @@ describe('copyFiles', () => {
|
|
|
406
434
|
})
|
|
407
435
|
|
|
408
436
|
it('should cleanup nested empty directories', async () => {
|
|
409
|
-
|
|
410
|
-
|
|
437
|
+
mockFg
|
|
438
|
+
.mockResolvedValueOnce(['nested/deeply/file.ts']) // First scan finds existing managed file
|
|
411
439
|
.mockResolvedValueOnce([]) // Second scan finds no files in packages
|
|
412
440
|
|
|
413
441
|
// Mock existing managed file
|
|
414
|
-
|
|
442
|
+
mockFs.readFile.mockImplementation((filePath) => {
|
|
415
443
|
if (filePath === path.join(mockCwd, 'nested/deeply/file.ts')) {
|
|
416
|
-
return Promise.resolve(Buffer.from('// managed by: graphcommerce\ncontent'))
|
|
444
|
+
return Promise.resolve(Buffer.from('// managed by: graphcommerce\ncontent')) as ReturnType<
|
|
445
|
+
typeof fs.readFile
|
|
446
|
+
>
|
|
417
447
|
}
|
|
418
448
|
if (filePath === path.join(mockCwd, '.gitignore')) {
|
|
419
|
-
return Promise.resolve('existing\ngitignore\ncontent')
|
|
449
|
+
return Promise.resolve('existing\ngitignore\ncontent') as ReturnType<typeof fs.readFile>
|
|
420
450
|
}
|
|
421
451
|
return Promise.reject(new Error(`ENOENT: no such file or directory, open '${filePath}'`))
|
|
422
452
|
})
|
|
423
453
|
|
|
424
454
|
// Mock directory checks to simulate empty directories
|
|
425
|
-
|
|
455
|
+
mockFs.readdir.mockReturnValue(Promise.resolve([]) as ReturnType<typeof fs.readdir>)
|
|
426
456
|
|
|
427
457
|
await copyFiles()
|
|
428
458
|
|
|
429
459
|
// Verify file was removed
|
|
430
|
-
expect(
|
|
460
|
+
expect(mockFs.unlink).toHaveBeenCalledWith(path.join(mockCwd, 'nested/deeply/file.ts'))
|
|
431
461
|
|
|
432
462
|
// Verify directories were checked and removed in the correct order
|
|
433
|
-
const readdirCalls =
|
|
434
|
-
const rmdirCalls =
|
|
463
|
+
const readdirCalls = mockFs.readdir.mock.calls.map((call) => call[0])
|
|
464
|
+
const rmdirCalls = mockFs.rmdir.mock.calls.map((call) => call[0])
|
|
435
465
|
|
|
436
466
|
// Both directories should have been checked
|
|
437
467
|
expect(readdirCalls).toContain(path.join(mockCwd, 'nested/deeply'))
|
|
@@ -448,54 +478,60 @@ describe('copyFiles', () => {
|
|
|
448
478
|
})
|
|
449
479
|
|
|
450
480
|
it('should handle partial directory cleanup', async () => {
|
|
451
|
-
|
|
452
|
-
|
|
481
|
+
mockFg
|
|
482
|
+
.mockResolvedValueOnce(['nested/remove.ts']) // First scan finds existing managed file
|
|
453
483
|
.mockResolvedValueOnce([]) // Second scan finds no files in packages
|
|
454
484
|
|
|
455
485
|
// Mock existing managed file
|
|
456
|
-
|
|
486
|
+
mockFs.readFile.mockImplementation((filePath) => {
|
|
457
487
|
if (filePath === path.join(mockCwd, 'nested/remove.ts')) {
|
|
458
|
-
return Promise.resolve(
|
|
488
|
+
return Promise.resolve(
|
|
489
|
+
Buffer.from('// managed by: graphcommerce\nremove content'),
|
|
490
|
+
) as ReturnType<typeof fs.readFile>
|
|
459
491
|
}
|
|
460
492
|
if (filePath === path.join(mockCwd, '.gitignore')) {
|
|
461
|
-
return Promise.resolve('existing\ngitignore\ncontent')
|
|
493
|
+
return Promise.resolve('existing\ngitignore\ncontent') as ReturnType<typeof fs.readFile>
|
|
462
494
|
}
|
|
463
495
|
return Promise.reject(new Error(`ENOENT: no such file or directory, open '${filePath}'`))
|
|
464
496
|
})
|
|
465
497
|
|
|
466
498
|
// Mock directory check to show directory is not empty
|
|
467
|
-
|
|
499
|
+
mockFs.readdir.mockReturnValue(
|
|
500
|
+
Promise.resolve(['other-file.ts']) as ReturnType<typeof fs.readdir>,
|
|
501
|
+
)
|
|
468
502
|
|
|
469
503
|
await copyFiles()
|
|
470
504
|
|
|
471
505
|
// Verify file removal
|
|
472
|
-
expect(
|
|
473
|
-
expect(
|
|
506
|
+
expect(mockFs.unlink).toHaveBeenCalledTimes(1)
|
|
507
|
+
expect(mockFs.unlink).toHaveBeenCalledWith(path.join(mockCwd, 'nested/remove.ts'))
|
|
474
508
|
|
|
475
509
|
// Verify directory was checked but not removed (since it's not empty)
|
|
476
|
-
expect(
|
|
477
|
-
expect(
|
|
510
|
+
expect(mockFs.readdir).toHaveBeenCalledWith(path.join(mockCwd, 'nested'))
|
|
511
|
+
expect(mockFs.rmdir).not.toHaveBeenCalled()
|
|
478
512
|
})
|
|
479
513
|
|
|
480
514
|
it('should handle directory removal errors gracefully', async () => {
|
|
481
|
-
|
|
482
|
-
|
|
515
|
+
mockFg
|
|
516
|
+
.mockResolvedValueOnce(['nested/file.ts']) // First scan finds existing managed file
|
|
483
517
|
.mockResolvedValueOnce([]) // Second scan finds no files in packages
|
|
484
518
|
|
|
485
519
|
// Mock existing managed file
|
|
486
|
-
|
|
520
|
+
mockFs.readFile.mockImplementation((filePath) => {
|
|
487
521
|
if (filePath === path.join(mockCwd, 'nested/file.ts')) {
|
|
488
|
-
return Promise.resolve(Buffer.from('// managed by: graphcommerce\ncontent'))
|
|
522
|
+
return Promise.resolve(Buffer.from('// managed by: graphcommerce\ncontent')) as ReturnType<
|
|
523
|
+
typeof fs.readFile
|
|
524
|
+
>
|
|
489
525
|
}
|
|
490
526
|
if (filePath === path.join(mockCwd, '.gitignore')) {
|
|
491
|
-
return Promise.resolve('existing\ngitignore\ncontent')
|
|
527
|
+
return Promise.resolve('existing\ngitignore\ncontent') as ReturnType<typeof fs.readFile>
|
|
492
528
|
}
|
|
493
529
|
return Promise.reject(new Error(`ENOENT: no such file or directory, open '${filePath}'`))
|
|
494
530
|
})
|
|
495
531
|
|
|
496
532
|
// Mock directory is empty but removal fails with EACCES
|
|
497
|
-
|
|
498
|
-
|
|
533
|
+
mockFs.readdir.mockReturnValue(Promise.resolve([]) as ReturnType<typeof fs.readdir>)
|
|
534
|
+
mockFs.rmdir.mockImplementation(() => {
|
|
499
535
|
const error = new Error('EACCES: permission denied') as NodeJS.ErrnoException
|
|
500
536
|
error.code = 'EACCES'
|
|
501
537
|
return Promise.reject(error)
|
|
@@ -504,7 +540,7 @@ describe('copyFiles', () => {
|
|
|
504
540
|
await copyFiles()
|
|
505
541
|
|
|
506
542
|
// Verify file removal still succeeded
|
|
507
|
-
expect(
|
|
543
|
+
expect(mockFs.unlink).toHaveBeenCalledWith(path.join(mockCwd, 'nested/file.ts'))
|
|
508
544
|
|
|
509
545
|
// Verify error was logged but didn't crash the process
|
|
510
546
|
expect(consoleError).toHaveBeenCalledWith(
|
|
@@ -737,14 +737,6 @@ it('finds plugins', () => {
|
|
|
737
737
|
"targetModule": "@graphcommerce/graphql",
|
|
738
738
|
"type": "function",
|
|
739
739
|
},
|
|
740
|
-
{
|
|
741
|
-
"enabled": true,
|
|
742
|
-
"sourceExport": "meshConfig",
|
|
743
|
-
"sourceModule": "@graphcommerce/graphql-mesh/plugins/meshConfigFake",
|
|
744
|
-
"targetExport": "meshConfig",
|
|
745
|
-
"targetModule": "@graphcommerce/graphql-mesh/meshConfig",
|
|
746
|
-
"type": "function",
|
|
747
|
-
},
|
|
748
740
|
]
|
|
749
741
|
`)
|
|
750
742
|
expect(disabled).toMatchInlineSnapshot(
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { vi } from 'vitest'
|
|
1
2
|
import type { GraphCommerceConfig } from '../../src/generated/config'
|
|
2
3
|
import { findOriginalSource } from '../../src/interceptors/findOriginalSource'
|
|
3
4
|
import { generateInterceptors } from '../../src/interceptors/generateInterceptors'
|
|
@@ -524,9 +525,9 @@ export const plugin: FunctionPlugin<typeof getSitemapPathsType> = (prev, ...args
|
|
|
524
525
|
return prev(...args)
|
|
525
526
|
}
|
|
526
527
|
`
|
|
527
|
-
console.error =
|
|
528
|
+
console.error = vi.fn()
|
|
528
529
|
const plugins = parseStructure(parseSync(src), fakeconfig, './plugins/MyPlugin.tsx')
|
|
529
|
-
expect((console.error as
|
|
530
|
+
expect((console.error as ReturnType<typeof vi.fn>).mock.calls[0][0]).toMatchInlineSnapshot(
|
|
530
531
|
'"Plugin configuration invalid! See ./plugins/MyPlugin.tsx"',
|
|
531
532
|
)
|
|
532
533
|
expect(plugins).toMatchInlineSnapshot('[]')
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import 'cosmiconfig';
|
|
2
2
|
import '../generated/config.js';
|
|
3
|
-
export { l as loadConfig, r as replaceConfigInString } from '../loadConfig-
|
|
3
|
+
export { l as loadConfig, r as replaceConfigInString } from '../loadConfig-CZYUe1jE.js';
|
|
4
4
|
import 'zod';
|
|
5
|
-
import '@apollo/client/utilities/
|
|
5
|
+
import '@apollo/client/utilities/internal';
|
|
6
6
|
import 'chalk';
|
|
7
7
|
import 'lodash';
|
package/dist/generated/config.js
CHANGED
|
@@ -39,14 +39,12 @@ function GraphCommerceConfigSchema() {
|
|
|
39
39
|
customerXMagentoCacheIdDisable: z.boolean().nullish(),
|
|
40
40
|
dataLayer: z.lazy(() => DatalayerConfigSchema().nullish()),
|
|
41
41
|
debug: z.lazy(() => GraphCommerceDebugConfigSchema().nullish()),
|
|
42
|
-
demoMode: z.boolean().default(true).nullish(),
|
|
43
42
|
enableGuestCheckoutLogin: z.boolean().nullish(),
|
|
44
43
|
googleAnalyticsId: z.string().nullish(),
|
|
45
44
|
googlePlaystore: z.lazy(() => GraphCommerceGooglePlaystoreConfigSchema().nullish()),
|
|
46
45
|
googleRecaptchaKey: z.string().nullish(),
|
|
47
46
|
googleTagmanagerId: z.string().nullish(),
|
|
48
47
|
graphqlMeshEditMode: z.boolean().default(false).nullish(),
|
|
49
|
-
hygraphEndpoint: z.string().min(1),
|
|
50
48
|
hygraphManagementApi: z.string().nullish(),
|
|
51
49
|
hygraphProjectId: z.string().nullish(),
|
|
52
50
|
hygraphWriteAccessToken: z.string().nullish(),
|
|
@@ -101,7 +99,6 @@ function GraphCommerceStorefrontConfigSchema() {
|
|
|
101
99
|
googleAnalyticsId: z.string().nullish(),
|
|
102
100
|
googleRecaptchaKey: z.string().nullish(),
|
|
103
101
|
googleTagmanagerId: z.string().nullish(),
|
|
104
|
-
hygraphLocales: z.array(z.string().min(1)).nullish(),
|
|
105
102
|
linguiLocale: z.string().nullish(),
|
|
106
103
|
locale: z.string().min(1),
|
|
107
104
|
magentoStoreCode: z.string().min(1),
|
package/dist/index.js
CHANGED
|
@@ -4,10 +4,11 @@ import path from 'path';
|
|
|
4
4
|
import { glob, sync } from 'glob';
|
|
5
5
|
import { findParentPath } from './utils/findParentPath.js';
|
|
6
6
|
import { spawn } from 'child_process';
|
|
7
|
-
import { l as loadConfig, t as toEnvStr } from './loadConfig-
|
|
8
|
-
export { r as replaceConfigInString } from './loadConfig-
|
|
7
|
+
import { l as loadConfig, t as toEnvStr } from './loadConfig-CZYUe1jE.js';
|
|
8
|
+
export { r as replaceConfigInString } from './loadConfig-CZYUe1jE.js';
|
|
9
9
|
import { parseFileSync, parseSync as parseSync$1, transformFileSync } from '@swc/core';
|
|
10
10
|
import fs$1, { writeFileSync, readFileSync, existsSync, rmSync, mkdirSync } from 'fs';
|
|
11
|
+
import { resolve as resolve$2 } from 'import-meta-resolve';
|
|
11
12
|
import assert from 'assert';
|
|
12
13
|
import crypto from 'crypto';
|
|
13
14
|
import lodash from 'lodash';
|
|
@@ -19,7 +20,7 @@ import { generate } from '@graphql-codegen/cli';
|
|
|
19
20
|
import { GraphCommerceConfigSchema } from './generated/config.js';
|
|
20
21
|
export { GraphCommerceDebugConfigSchema, GraphCommerceStorefrontConfigSchema } from './generated/config.js';
|
|
21
22
|
import 'cosmiconfig';
|
|
22
|
-
import '@apollo/client/utilities/
|
|
23
|
+
import '@apollo/client/utilities/internal';
|
|
23
24
|
import 'chalk';
|
|
24
25
|
|
|
25
26
|
async function fsExists(file) {
|
|
@@ -248,7 +249,7 @@ function sig() {
|
|
|
248
249
|
|
|
249
250
|
const resolveCache = /* @__PURE__ */ new Map();
|
|
250
251
|
function findPackageJson(id, root) {
|
|
251
|
-
let dir = id.startsWith("/") ? id : import.meta.
|
|
252
|
+
let dir = id.startsWith("/") ? id : resolve$2(id, import.meta.url);
|
|
252
253
|
if (dir.startsWith("file://")) dir = new URL(dir).pathname;
|
|
253
254
|
let packageJsonLocation = path.join(dir, "package.json");
|
|
254
255
|
while (!fs$1.existsSync(packageJsonLocation)) {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { cosmiconfigSync } from 'cosmiconfig';
|
|
2
2
|
import { GraphCommerceConfigSchema } from './generated/config.js';
|
|
3
|
-
import { cloneDeep, mergeDeep } from '@apollo/client/utilities/
|
|
3
|
+
import { cloneDeep, mergeDeep } from '@apollo/client/utilities/internal';
|
|
4
4
|
import chalk from 'chalk';
|
|
5
5
|
import lodash from 'lodash';
|
|
6
6
|
import { z, ZodEffects, ZodOptional, ZodNullable, ZodDefault, ZodLazy, ZodObject, ZodArray, ZodNumber, ZodString, ZodEnum, ZodBoolean } from 'zod';
|
package/package.json
CHANGED
|
@@ -2,9 +2,8 @@
|
|
|
2
2
|
"name": "@graphcommerce/next-config",
|
|
3
3
|
"homepage": "https://www.graphcommerce.org/",
|
|
4
4
|
"repository": "github:graphcommerce-org/graphcommerce",
|
|
5
|
-
"version": "10.0.0
|
|
5
|
+
"version": "10.0.0",
|
|
6
6
|
"type": "module",
|
|
7
|
-
"types": "./src/index.ts",
|
|
8
7
|
"exports": {
|
|
9
8
|
".": {
|
|
10
9
|
"types": "./src/index.ts",
|
|
@@ -44,16 +43,16 @@
|
|
|
44
43
|
},
|
|
45
44
|
"dependencies": {
|
|
46
45
|
"@graphql-codegen/cli": "6.1.0",
|
|
47
|
-
"@swc/core": "1.15.
|
|
48
|
-
"@swc/wasm-web": "^1.15.
|
|
46
|
+
"@swc/core": "1.15.7",
|
|
47
|
+
"@swc/wasm-web": "^1.15.7",
|
|
49
48
|
"@types/js-yaml": "^4.0.9",
|
|
50
49
|
"@types/lodash": "^4.17.21",
|
|
51
50
|
"chalk": "^4.1.2",
|
|
52
51
|
"cosmiconfig": "^8.3.6",
|
|
53
52
|
"fast-glob": "^3.3.3",
|
|
54
|
-
"glob": "^10.
|
|
53
|
+
"glob": "^10.5.0",
|
|
55
54
|
"graphql": "^16.12.0",
|
|
56
|
-
"
|
|
55
|
+
"import-meta-resolve": "^4.1.0",
|
|
57
56
|
"js-yaml": "^4.1.1",
|
|
58
57
|
"js-yaml-loader": "^1.2.2",
|
|
59
58
|
"lodash": "^4.17.21",
|
|
@@ -64,7 +63,7 @@
|
|
|
64
63
|
},
|
|
65
64
|
"peerDependencies": {
|
|
66
65
|
"@apollo/client": "*",
|
|
67
|
-
"@graphcommerce/prettier-config-pwa": "^10.0.0
|
|
66
|
+
"@graphcommerce/prettier-config-pwa": "^10.0.0",
|
|
68
67
|
"@lingui/loader": "*",
|
|
69
68
|
"@lingui/macro": "*",
|
|
70
69
|
"@lingui/react": "*",
|
package/src/generated/config.ts
CHANGED
|
@@ -212,14 +212,6 @@ export type GraphCommerceConfig = {
|
|
|
212
212
|
dataLayer?: InputMaybe<DatalayerConfig>
|
|
213
213
|
/** Debug configuration for GraphCommerce */
|
|
214
214
|
debug?: InputMaybe<GraphCommerceDebugConfig>
|
|
215
|
-
/**
|
|
216
|
-
* Enables some demo specific code that is probably not useful for a project:
|
|
217
|
-
*
|
|
218
|
-
* - Adds the "BY GC" to the product list items.
|
|
219
|
-
* - Adds "dominant_color" attribute swatches to the product list items.
|
|
220
|
-
* - Creates a big list items in the product list.
|
|
221
|
-
*/
|
|
222
|
-
demoMode?: InputMaybe<Scalars['Boolean']['input']>
|
|
223
215
|
/**
|
|
224
216
|
* Enable Guest Checkout Login: During customer login, GraphCommerce queries Magento to determine
|
|
225
217
|
* whether the customer account already exists or not. If not, the sign-up form is shown instead.
|
|
@@ -264,14 +256,6 @@ export type GraphCommerceConfig = {
|
|
|
264
256
|
* developing new resolvers this should be set to true.
|
|
265
257
|
*/
|
|
266
258
|
graphqlMeshEditMode?: InputMaybe<Scalars['Boolean']['input']>
|
|
267
|
-
/**
|
|
268
|
-
* The Hygraph endpoint.
|
|
269
|
-
*
|
|
270
|
-
* > Read-only endpoint that allows low latency and high read-throughput content delivery.
|
|
271
|
-
*
|
|
272
|
-
* Project settings -> API Access -> High Performance Read-only Content API
|
|
273
|
-
*/
|
|
274
|
-
hygraphEndpoint: Scalars['String']['input']
|
|
275
259
|
/**
|
|
276
260
|
* Hygraph Management API. **Only used for migrations.**
|
|
277
261
|
*
|
|
@@ -483,11 +467,6 @@ export type GraphCommerceStorefrontConfig = {
|
|
|
483
467
|
googleRecaptchaKey?: InputMaybe<Scalars['String']['input']>
|
|
484
468
|
/** The Google Tagmanager ID to be used per locale. */
|
|
485
469
|
googleTagmanagerId?: InputMaybe<Scalars['String']['input']>
|
|
486
|
-
/**
|
|
487
|
-
* Add a gcms-locales header to make sure queries return in a certain language, can be an array to
|
|
488
|
-
* define fallbacks.
|
|
489
|
-
*/
|
|
490
|
-
hygraphLocales?: InputMaybe<Array<Scalars['String']['input']>>
|
|
491
470
|
/** Custom locale used to load the .po files. Must be a valid locale, also used for Intl functions. */
|
|
492
471
|
linguiLocale?: InputMaybe<Scalars['String']['input']>
|
|
493
472
|
/**
|
|
@@ -623,14 +602,12 @@ export function GraphCommerceConfigSchema(): z.ZodObject<Properties<GraphCommerc
|
|
|
623
602
|
customerXMagentoCacheIdDisable: z.boolean().nullish(),
|
|
624
603
|
dataLayer: z.lazy(() => DatalayerConfigSchema().nullish()),
|
|
625
604
|
debug: z.lazy(() => GraphCommerceDebugConfigSchema().nullish()),
|
|
626
|
-
demoMode: z.boolean().default(true).nullish(),
|
|
627
605
|
enableGuestCheckoutLogin: z.boolean().nullish(),
|
|
628
606
|
googleAnalyticsId: z.string().nullish(),
|
|
629
607
|
googlePlaystore: z.lazy(() => GraphCommerceGooglePlaystoreConfigSchema().nullish()),
|
|
630
608
|
googleRecaptchaKey: z.string().nullish(),
|
|
631
609
|
googleTagmanagerId: z.string().nullish(),
|
|
632
610
|
graphqlMeshEditMode: z.boolean().default(false).nullish(),
|
|
633
|
-
hygraphEndpoint: z.string().min(1),
|
|
634
611
|
hygraphManagementApi: z.string().nullish(),
|
|
635
612
|
hygraphProjectId: z.string().nullish(),
|
|
636
613
|
hygraphWriteAccessToken: z.string().nullish(),
|
|
@@ -697,7 +674,6 @@ export function GraphCommerceStorefrontConfigSchema(): z.ZodObject<
|
|
|
697
674
|
googleAnalyticsId: z.string().nullish(),
|
|
698
675
|
googleRecaptchaKey: z.string().nullish(),
|
|
699
676
|
googleTagmanagerId: z.string().nullish(),
|
|
700
|
-
hygraphLocales: z.array(z.string().min(1)).nullish(),
|
|
701
677
|
linguiLocale: z.string().nullish(),
|
|
702
678
|
locale: z.string().min(1),
|
|
703
679
|
magentoStoreCode: z.string().min(1),
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import fs from 'fs'
|
|
2
2
|
import path from 'path'
|
|
3
|
+
import { resolve as resolveModule } from 'import-meta-resolve'
|
|
3
4
|
import type { PackageJson } from 'type-fest'
|
|
4
5
|
import { PackagesSort } from './PackagesSort'
|
|
5
6
|
import { sig } from './sig'
|
|
@@ -10,7 +11,7 @@ type DependencyStructure = Record<string, { dirName: string; dependencies: strin
|
|
|
10
11
|
const resolveCache: Map<string, PackageNames> = new Map<string, PackageNames>()
|
|
11
12
|
|
|
12
13
|
function findPackageJson(id: string, root: string) {
|
|
13
|
-
let dir = id.startsWith('/') ? id : import.meta.
|
|
14
|
+
let dir = id.startsWith('/') ? id : resolveModule(id, import.meta.url)
|
|
14
15
|
|
|
15
16
|
if (dir.startsWith('file://')) dir = new URL(dir).pathname
|
|
16
17
|
|
package/src/withGraphCommerce.ts
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
// import CircularDependencyPlugin from 'circular-dependency-plugin'
|
|
2
|
-
// import { DuplicatesPlugin } from 'inspectpack/plugin'
|
|
3
1
|
import type { NextConfig } from 'next'
|
|
4
2
|
import type { DomainLocale } from 'next/dist/server/config'
|
|
5
3
|
import type { Configuration } from 'webpack'
|