diff --git a/packages/cli/src/utils/generate.test.ts b/packages/cli/src/utils/generate.test.ts new file mode 100644 index 0000000000..54b322bc99 --- /dev/null +++ b/packages/cli/src/utils/generate.test.ts @@ -0,0 +1,108 @@ +import { describe, expect, it } from 'vitest' +import { buildFaststorePackageJson } from './generate' + +describe('buildFaststorePackageJson', () => { + const coreManifest = { + name: '@faststore/core', + version: '4.1.2', + license: 'MIT', + browserslist: 'supports es6-module and not dead', + packageManager: 'pnpm@10.28.0', + exports: { + '.': './index.ts', + './api': './api/index.ts', + }, + scripts: { + test: 'vitest run', + 'test:e2e': 'cypress open', + generate: 'pnpm run gen-types && pnpm run cache-graphql ', + }, + dependencies: { + next: '^16.0.0', + react: '^18.2.0', + }, + devDependencies: { + vitest: 'catalog:', + }, + engines: { node: '>=20' }, + sideEffects: false, + } + + it('strips the `packageManager` field so Yarn / Corepack do not mangle it in stores', () => { + const result = buildFaststorePackageJson(coreManifest) + + expect(result).not.toHaveProperty('packageManager') + }) + + it('strips the `exports` field so it does not shadow @faststore/core resolution', () => { + const result = buildFaststorePackageJson(coreManifest) + + expect(result).not.toHaveProperty('exports') + }) + + it('renames the package to `dot-faststore`', () => { + const result = buildFaststorePackageJson(coreManifest) + + expect(result.name).toBe('dot-faststore') + }) + + it('overrides the scripts needed by the CLI on top of any pre-existing scripts', () => { + const result = buildFaststorePackageJson(coreManifest) + + expect(result.scripts).toEqual({ + 'test:e2e': 'cypress open', + test: 'vitest run', + generate: 'faststore generate', + build: 'next build --webpack', + serve: 'next serve', + dev: 'next dev --webpack', + 'dev-only': 'next dev --webpack', + predev: 'na run partytown', + prebuild: 'na run partytown', + }) + }) + + it('preserves dependencies, devDependencies, engines and other metadata fields', () => { + const result = buildFaststorePackageJson(coreManifest) + + expect(result.dependencies).toEqual(coreManifest.dependencies) + expect(result.devDependencies).toEqual(coreManifest.devDependencies) + expect(result.engines).toEqual(coreManifest.engines) + expect(result.version).toBe(coreManifest.version) + expect(result.license).toBe(coreManifest.license) + expect(result.browserslist).toBe(coreManifest.browserslist) + expect(result.sideEffects).toBe(false) + }) + + it('still injects the required scripts when the source manifest has no scripts entry', () => { + const { scripts: _, ...withoutScripts } = coreManifest + + const result = buildFaststorePackageJson(withoutScripts) + + expect(result.scripts).toEqual({ + generate: 'faststore generate', + build: 'next build --webpack', + serve: 'next serve', + dev: 'next dev --webpack', + 'dev-only': 'next dev --webpack', + predev: 'na run partytown', + prebuild: 'na run partytown', + }) + }) + + it('omits `packageManager` from the output even when it is absent from the source', () => { + const { packageManager: _, ...withoutPackageManager } = coreManifest + + const result = buildFaststorePackageJson(withoutPackageManager) + + expect(result).not.toHaveProperty('packageManager') + }) + + it('does not mutate the input manifest', () => { + const input = structuredClone(coreManifest) + + buildFaststorePackageJson(input) + + expect(input).toEqual(coreManifest) + }) +}) diff --git a/packages/cli/src/utils/generate.ts b/packages/cli/src/utils/generate.ts index 23953871bc..cea5a8d752 100644 --- a/packages/cli/src/utils/generate.ts +++ b/packages/cli/src/utils/generate.ts @@ -55,6 +55,39 @@ function createTmpFolder(basePath: string) { } } +/** + * Builds the `.faststore/package.json` from `@faststore/core`'s manifest. + * Strips `exports` and `packageManager` (the latter is pinned to pnpm and + * breaks Yarn/Corepack on consumer stores). + */ +export function buildFaststorePackageJson( + coreManifest: Record +): Record { + const { + exports: _exports, + packageManager: _packageManager, + ...rest + } = coreManifest + + const existingScripts = + (rest.scripts as Record | undefined) ?? {} + + return { + ...rest, + name: 'dot-faststore', + scripts: { + ...existingScripts, + generate: 'faststore generate', + build: 'next build --webpack', + serve: 'next serve', + dev: 'next dev --webpack', + 'dev-only': 'next dev --webpack', + predev: 'na run partytown', + prebuild: 'na run partytown', + }, + } +} + /** * Prevents imports from @faststore/core from randomly conflicting * where sometimes the package.json from the .faststore folder @@ -63,21 +96,11 @@ function createTmpFolder(basePath: string) { function filterAndCopyPackageJson(basePath: string) { const { coreDir, tmpDir } = withBasePath(basePath) - const { exports: _, ...filteredFileContent } = JSON.parse( + const coreManifest = JSON.parse( readFileSync(path.join(coreDir, 'package.json'), 'utf8') ) - filteredFileContent.name = 'dot-faststore' - filteredFileContent.scripts = { - ...filteredFileContent.scripts, - generate: 'faststore generate', - build: 'next build --webpack', - serve: 'next serve', - dev: 'next dev --webpack', - 'dev-only': 'next dev --webpack', - predev: 'na run partytown', - prebuild: 'na run partytown', - } + const filteredFileContent = buildFaststorePackageJson(coreManifest) writeJsonSync(path.join(tmpDir, 'package.json'), filteredFileContent, { spaces: 2, diff --git a/packages/core/next.config.js b/packages/core/next.config.js index 3c9b846c97..0121c011b1 100644 --- a/packages/core/next.config.js +++ b/packages/core/next.config.js @@ -22,8 +22,9 @@ console.log(` * */ const nextConfig = { /* config options here */ - /* Replaces terser by swc for minifying. It's the default in NextJS 13 */ - swcMinify: true, + // Type checking is run separately; skipping here keeps the build green when + // faststore packages are linked locally for development. + typescript: { ignoreBuildErrors: true }, ...(Array.isArray(storeConfig.experimental?.transpilePackages) && storeConfig.experimental.transpilePackages.length > 0 && { transpilePackages: storeConfig.experimental.transpilePackages, @@ -55,9 +56,7 @@ const nextConfig = { sassOptions: { silenceDeprecations: ['if-function', 'legacy-js-api'], }, - // TODO: We won't need to enable this experimental feature when migrating to Next.js 13 experimental: { - instrumentationHook: true, scrollRestoration: !storeConfig.experimental.scrollRestoration, }, /*