Files
pnpm/pnpm/test/syncInjectedDepsAfterScripts.ts
Khải ae43ac79fa feat(cli/config)!: breaking changes (#9854)
* feat(cli/config)!: breaking changes

* refactor(test): remove `deepNullProto` and reuse `getOutputString`

* feat(cli/config/list): censor protected settings

* test: censorship of protected settings

* docs(changeset): censorship of protected settings

* fix: eslint

* feat(config)!: exclude non-option from `rawConfig`

* fix: eslint

* refactor: move default registries to builtin (#9886)

* feat(config)!: filter rc settings

* feat(config): don't exclude non-options

This reverts commit a79f72dbfb.

* feat(cli/config/get)!: print array as json

* test: fix

* docs(changeset): correct

* test: fix

* feat(cli/config)!: only kebabize option fields (wip)

* chore(git): revert the implementation

This reverts commit 529f9bdd47.

* test: restore

This reverts commit d8191e0ed8.

* feat(cli/config)!: only kebabize option fields (wip)

* feat: use `types` instead

* feat(cli/config/get)!: only kebabize rc fields

* test: add

* test: non-rc kebab-case keys

* test: correct

* docs(changeset): correct

* docs(changeset): style

* docs(changeset): correct

* test: only kebabize rc fields

* fix: import path

* fix: eslint

* fix: `isCamelCase`

* feat(cli/config/set)!: forbid unknown rc fields

* test: fix existing test

* test: refuse unsupported rc settings

* feat: hint

* feat(cli/config/set)!: refuse kebab-case workspace-specific settings

* feat(config)!: ignore non-camelCase from `pnpm-workspace.yaml`

* test: config get

* test: config list

* refactor: extract shared code into its own package

* test: `isCamelCase`

* feat(cli/config/list)!: consistent naming cases

* refactor: make it more reusable

* feat(cli/config/get)!: consistent naming cases

* feat(cli/config/get): censor protected settings

* test: `get ''` should be the same as `list`

* docs(test): quotation marks

* refactor: remove unnecessary `test.each`

* docs(changeset): case changes

* test: unknown keys

* docs(changeset): correct

* docs(changeset): non camelCase from `pnpm-workspace.yaml`

* fix: eslint

* docs(changeset): correct terminology

* docs(changeset): clarify

* feat!: do not load non-auth and non-registry

* fix: implementation

* test: no hidden settings

* fix: eslint

* fix: do not drop default values

* test: fix

* test: remove irrelevant tests

* test: fix (wip)

* fix: auth

* test: skip an inapplicable test

* test: temporary skip a test

* test: fix 'respects testPattern'

* test: fix 'respects changedFilesIgnorePattern'

* test: fix 'changedFilesIgnorePattern is respected'

* test: rename a test

* test: fix `package-lock=false`

* feat: exception for `managePackageManagerVersions`

* test: `managePackageManagerVersions: false`

* test: fix (wip)

* test: workaround

* fix: default `optional` to `true`

* fix: `filter` on `pnpm-workspace.yaml`

* test: fix

* test: disable ones that no longer apply

* chore(git): revert incorrect change

* fix: `filter` on `pnpm-workspace.yaml` (#10127)

* fix: `filter` on `pnpm-workspace.yaml`

* docs: changeset

* fix: actual fix

* fix: don't set default on config

* docs(readme): correct a package description

* fix: typo

* test: fix

* test: use a field that wouldn't be ignored

* test: replace some `.npmrc` with `pnpm-workspace.yaml`

* docs(changeset): less awkward wordings

* docs(changeset): correction
2025-10-28 17:09:15 +01:00

255 lines
9.9 KiB
TypeScript

import fs from 'fs'
import path from 'path'
import { preparePackages } from '@pnpm/prepare'
import { fixtures } from '@pnpm/test-fixtures'
import { sync as writeYamlFile } from 'write-yaml-file'
import { execPnpm } from './utils/index.js'
const f = fixtures(import.meta.dirname)
const PKG_FILES = [
...fs.readdirSync(f.find('injected-dep-files')),
'package.json',
].sort()
function prepareInjectedDepsWorkspace (syncInjectedDepsAfterScripts: string[]) {
const scripts = {
build1: 'node ./build1.cjs',
build2: 'node ./build2.cjs',
build3: 'node ./build3.cjs',
}
preparePackages([
{
name: 'foo',
version: '0.0.0',
dependencies: {
'is-positive': '1.0.0',
},
scripts,
},
{
name: 'bar',
version: '0.0.0',
dependencies: {
foo: 'workspace:*',
},
scripts,
},
{
name: 'baz',
version: '0.0.0',
dependencies: {
bar: 'workspace:*',
},
scripts,
},
])
for (const pkgName of ['foo', 'bar', 'baz']) {
f.copy('injected-dep-files', pkgName)
}
writeYamlFile('pnpm-workspace.yaml', {
packages: ['*'],
reporter: 'append-only',
injectWorkspacePackages: true,
dedupeInjectedDeps: false,
syncInjectedDepsAfterScripts,
})
}
test('with sync-injected-deps-after-scripts', async () => {
prepareInjectedDepsWorkspace(['build1', 'build2', 'build3'])
await execPnpm(['install'])
expect(fs.readdirSync('node_modules/.pnpm')).toContain('foo@file+foo')
expect(fs.readdirSync('node_modules/.pnpm')).toContain('bar@file+bar')
expect(fs.readdirSync('node_modules/.pnpm/foo@file+foo/node_modules/foo').sort()).toStrictEqual(PKG_FILES)
expect(
fs.readFileSync('node_modules/.pnpm/foo@file+foo/node_modules/foo/should-be-modified-by-build1.txt', 'utf-8')
).toBe('Before modification\n')
expect(fs.readdirSync('node_modules/.pnpm/bar@file+bar/node_modules/bar').sort()).toStrictEqual(PKG_FILES)
expect(
fs.readFileSync('node_modules/.pnpm/bar@file+bar/node_modules/bar/should-be-modified-by-build1.txt', 'utf-8')
).toBe('Before modification\n')
// build1 should update the injected files
{
await execPnpm(['--recursive', 'run', 'build1'])
// injected foo
expect(fs.readdirSync('node_modules/.pnpm/foo@file+foo/node_modules/foo')).not.toContain('should-be-deleted-by-build1.txt')
expect(
fs.readFileSync('node_modules/.pnpm/foo@file+foo/node_modules/foo/should-be-added-by-build1.txt', 'utf-8')
).toBe(path.resolve('foo/build1.cjs'))
expect(
fs.readFileSync('node_modules/.pnpm/foo@file+foo/node_modules/foo/should-be-modified-by-build1.txt', 'utf-8')
).toBe('After modification')
// injected bar
expect(fs.readdirSync('node_modules/.pnpm/bar@file+bar/node_modules/bar')).not.toContain('should-be-deleted-by-build1.txt')
expect(
fs.readFileSync('node_modules/.pnpm/bar@file+bar/node_modules/bar/should-be-added-by-build1.txt', 'utf-8')
).toBe(path.resolve('bar/build1.cjs'))
expect(
fs.readFileSync('node_modules/.pnpm/bar@file+bar/node_modules/bar/should-be-modified-by-build1.txt', 'utf-8')
).toBe('After modification')
}
// build2 should update the injected files
{
await execPnpm(['--recursive', 'run', 'build2'])
// injected foo
expect(
fs.readFileSync('node_modules/.pnpm/foo@file+foo/node_modules/foo/created-by-build2.txt', 'utf-8')
).toBe(path.resolve('foo/build2.cjs'))
// injected bar
expect(
fs.readFileSync('node_modules/.pnpm/bar@file+bar/node_modules/bar/created-by-build2.txt', 'utf-8')
).toBe(path.resolve('bar/build2.cjs'))
}
})
test('without sync-injected-deps-after-scripts', async () => {
prepareInjectedDepsWorkspace([])
await execPnpm(['install'])
expect(fs.readdirSync('node_modules/.pnpm')).toContain('foo@file+foo')
expect(fs.readdirSync('node_modules/.pnpm')).toContain('bar@file+bar')
expect(fs.readdirSync('node_modules/.pnpm/foo@file+foo/node_modules/foo').sort()).toStrictEqual(PKG_FILES)
expect(
fs.readFileSync('node_modules/.pnpm/foo@file+foo/node_modules/foo/should-be-modified-by-build1.txt', 'utf-8')
).toBe('Before modification\n')
expect(fs.readdirSync('node_modules/.pnpm/bar@file+bar/node_modules/bar').sort()).toStrictEqual(PKG_FILES)
expect(
fs.readFileSync('node_modules/.pnpm/bar@file+bar/node_modules/bar/should-be-modified-by-build1.txt', 'utf-8')
).toBe('Before modification\n')
// build1 should not update the injected files
{
await execPnpm(['--recursive', 'run', 'build1'])
// injected foo
expect(fs.readdirSync('node_modules/.pnpm/foo@file+foo/node_modules/foo')).toContain('should-be-deleted-by-build1.txt')
expect(fs.readdirSync('node_modules/.pnpm/foo@file+foo/node_modules/foo')).not.toContain('should-be-added-by-build1.txt')
// injected bar
expect(fs.readdirSync('node_modules/.pnpm/bar@file+bar/node_modules/bar')).toContain('should-be-deleted-by-build1.txt')
expect(fs.readdirSync('node_modules/.pnpm/bar@file+bar/node_modules/bar')).not.toContain('should-be-added-by-build1.txt')
}
// build2 should not update the injected files
{
await execPnpm(['--recursive', 'run', 'build2'])
// injected foo
expect(fs.readdirSync('node_modules/.pnpm/foo@file+foo/node_modules/foo')).not.toContain('created-by-build2.txt')
// injected bar
expect(fs.readdirSync('node_modules/.pnpm/bar@file+bar/node_modules/bar')).not.toContain('created-by-build2.txt')
}
})
test('filter scripts', async () => {
prepareInjectedDepsWorkspace(['build1'])
await execPnpm(['install'])
expect(fs.readdirSync('node_modules/.pnpm')).toContain('foo@file+foo')
expect(fs.readdirSync('node_modules/.pnpm')).toContain('bar@file+bar')
expect(fs.readdirSync('node_modules/.pnpm/foo@file+foo/node_modules/foo').sort()).toStrictEqual(PKG_FILES)
expect(
fs.readFileSync('node_modules/.pnpm/foo@file+foo/node_modules/foo/should-be-modified-by-build1.txt', 'utf-8')
).toBe('Before modification\n')
expect(fs.readdirSync('node_modules/.pnpm/bar@file+bar/node_modules/bar').sort()).toStrictEqual(PKG_FILES)
expect(
fs.readFileSync('node_modules/.pnpm/bar@file+bar/node_modules/bar/should-be-modified-by-build1.txt', 'utf-8')
).toBe('Before modification\n')
// build1 should update the injected files
{
await execPnpm(['--recursive', 'run', 'build1'])
// injected foo
expect(fs.readdirSync('node_modules/.pnpm/foo@file+foo/node_modules/foo')).not.toContain('should-be-deleted-by-build1.txt')
expect(
fs.readFileSync('node_modules/.pnpm/foo@file+foo/node_modules/foo/should-be-added-by-build1.txt', 'utf-8')
).toBe(path.resolve('foo/build1.cjs'))
expect(
fs.readFileSync('node_modules/.pnpm/foo@file+foo/node_modules/foo/should-be-modified-by-build1.txt', 'utf-8')
).toBe('After modification')
// injected bar
expect(fs.readdirSync('node_modules/.pnpm/bar@file+bar/node_modules/bar')).not.toContain('should-be-deleted-by-build1.txt')
expect(
fs.readFileSync('node_modules/.pnpm/bar@file+bar/node_modules/bar/should-be-added-by-build1.txt', 'utf-8')
).toBe(path.resolve('bar/build1.cjs'))
expect(
fs.readFileSync('node_modules/.pnpm/bar@file+bar/node_modules/bar/should-be-modified-by-build1.txt', 'utf-8')
).toBe('After modification')
}
// build2 should not update the injected files
{
await execPnpm(['--recursive', 'run', 'build2'])
// injected foo
expect(fs.readdirSync('node_modules/.pnpm/foo@file+foo/node_modules/foo')).not.toContain('created-by-build2.txt')
// injected bar
expect(fs.readdirSync('node_modules/.pnpm/bar@file+bar/node_modules/bar')).not.toContain('created-by-build2.txt')
}
})
test('directories and symlinks', async () => {
prepareInjectedDepsWorkspace(['build1', 'build2', 'build3'])
await execPnpm(['install'])
expect(fs.readdirSync('node_modules/.pnpm')).toContain('foo@file+foo')
expect(fs.readdirSync('node_modules/.pnpm')).toContain('bar@file+bar')
expect(fs.readdirSync('node_modules/.pnpm/foo@file+foo/node_modules/foo').sort()).toStrictEqual(PKG_FILES)
expect(fs.readdirSync('node_modules/.pnpm/bar@file+bar/node_modules/bar').sort()).toStrictEqual(PKG_FILES)
// build3 should update the injected files
{
await execPnpm(['--filter=foo', 'run', 'build3'])
// should create empty-dirs at source
expect(fs.readdirSync('foo/empty-dirs/a/a')).toStrictEqual([])
expect(fs.readdirSync('foo/empty-dirs/a/b')).toStrictEqual([])
expect(fs.readdirSync('foo/empty-dirs/b/a')).toStrictEqual([])
expect(fs.readdirSync('foo/empty-dirs/b/b')).toStrictEqual([])
// should not create empty-dirs at the injected location
expect(fs.readdirSync('node_modules/.pnpm/foo@file+foo/node_modules/foo')).not.toContain('empty-dirs')
// should recreate a directories tree at the injected location
expect(fs.readdirSync('node_modules/.pnpm/foo@file+foo/node_modules/foo')).toContain('files')
expect(
fs.readdirSync('node_modules/.pnpm/foo@file+foo/node_modules/foo/files')
.sort()
).toStrictEqual(['foo', 'foo_bar.txt'])
expect(
fs.readdirSync('node_modules/.pnpm/foo@file+foo/node_modules/foo/files/foo')
.sort()
).toStrictEqual(['bar.txt', 'foo'])
expect(
fs.readdirSync('node_modules/.pnpm/foo@file+foo/node_modules/foo/files/foo/foo')
.sort()
).toStrictEqual(['foo.txt'])
// should recreate the structure of the symlinks at the injected location
// NOTE: The current implementation of @pnpm/directory-fetcher would treat symlinks to dir at real dir
// because it uses fs.stat instead of fs.lstat, so testing with fs.realpathSync wouldn't work.
expect(fs.readFileSync('node_modules/.pnpm/foo@file+foo/node_modules/foo/link-to-a-file', 'utf-8')).toBe('This is foo_bar')
expect(
fs.readdirSync('node_modules/.pnpm/foo@file+foo/node_modules/foo/link-to-a-dir')
.sort()
).toStrictEqual(
fs.readdirSync('node_modules/.pnpm/foo@file+foo/node_modules/foo/files/foo')
.sort()
)
}
})