Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
108 changes: 108 additions & 0 deletions packages/cli/src/utils/generate.test.ts
Original file line number Diff line number Diff line change
@@ -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)
})
})
47 changes: 35 additions & 12 deletions packages/cli/src/utils/generate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<string, unknown>
): Record<string, unknown> {
const {
exports: _exports,
packageManager: _packageManager,
...rest
} = coreManifest

const existingScripts =
(rest.scripts as Record<string, string> | 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
Expand All @@ -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,
Expand Down
7 changes: 3 additions & 4 deletions packages/core/next.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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,
},
/*
Expand Down
Loading