mirror of
https://github.com/pnpm/pnpm.git
synced 2026-03-30 21:11:55 -04:00
581 lines
18 KiB
TypeScript
581 lines
18 KiB
TypeScript
import fs from 'fs'
|
|
import path from 'path'
|
|
import { LOCKFILE_VERSION } from '@pnpm/constants'
|
|
import { type LockfileFile } from '@pnpm/lockfile.fs'
|
|
import { prepareEmpty, preparePackages } from '@pnpm/prepare'
|
|
import { addDistTag } from '@pnpm/registry-mock'
|
|
import { fixtures } from '@pnpm/test-fixtures'
|
|
import { type ProjectRootDir } from '@pnpm/types'
|
|
import {
|
|
addDependenciesToPackage,
|
|
install,
|
|
mutateModules,
|
|
type MutatedProject,
|
|
mutateModulesInSingleProject,
|
|
type ProjectOptions,
|
|
} from '@pnpm/core'
|
|
import { sync as rimraf } from '@zkochan/rimraf'
|
|
import normalizePath from 'normalize-path'
|
|
import { sync as readYamlFile } from 'read-yaml-file'
|
|
import symlinkDir from 'symlink-dir'
|
|
import { testDefaults } from '../utils/index.js'
|
|
|
|
const f = fixtures(import.meta.dirname)
|
|
|
|
test('scoped modules from a directory', async () => {
|
|
const project = prepareEmpty()
|
|
await addDependenciesToPackage({}, [`file:${f.find('local-scoped-pkg')}`], testDefaults())
|
|
|
|
const m = project.requireModule('@scope/local-scoped-pkg')
|
|
|
|
expect(m()).toBe('@scope/local-scoped-pkg')
|
|
})
|
|
|
|
test('local file', async () => {
|
|
const project = prepareEmpty()
|
|
f.copy('local-pkg', path.resolve('..', 'local-pkg'))
|
|
|
|
const { updatedManifest: manifest } = await addDependenciesToPackage({}, ['link:../local-pkg'], testDefaults())
|
|
|
|
const expectedSpecs = { 'local-pkg': `link:..${path.sep}local-pkg` }
|
|
expect(manifest.dependencies).toStrictEqual(expectedSpecs)
|
|
|
|
const m = project.requireModule('local-pkg')
|
|
|
|
expect(m).toBeTruthy()
|
|
|
|
const lockfile = project.readLockfile()
|
|
|
|
expect(lockfile).toStrictEqual({
|
|
settings: {
|
|
autoInstallPeers: true,
|
|
excludeLinksFromLockfile: false,
|
|
},
|
|
importers: {
|
|
'.': {
|
|
dependencies: {
|
|
'local-pkg': {
|
|
specifier: expectedSpecs['local-pkg'],
|
|
version: 'link:../local-pkg',
|
|
},
|
|
},
|
|
},
|
|
},
|
|
lockfileVersion: LOCKFILE_VERSION,
|
|
})
|
|
})
|
|
|
|
test('a symlink to a symlink to a local dependency is preserved', async () => {
|
|
prepareEmpty()
|
|
const localPkgDir = path.resolve('..', 'local-pkg')
|
|
f.copy('local-pkg', localPkgDir)
|
|
await symlinkDir(localPkgDir, path.resolve('../symlink'))
|
|
|
|
await addDependenciesToPackage({}, ['link:../symlink'], testDefaults())
|
|
|
|
expect(fs.readlinkSync(path.resolve('node_modules/local-pkg'))).toContain('symlink')
|
|
})
|
|
|
|
test('local directory with no package.json', async () => {
|
|
const project = prepareEmpty()
|
|
fs.mkdirSync('pkg')
|
|
fs.writeFileSync('pkg/index.js', 'hello', 'utf8')
|
|
|
|
const { updatedManifest: manifest } = await addDependenciesToPackage({}, ['file:./pkg'], testDefaults())
|
|
|
|
const expectedSpecs = { pkg: 'file:pkg' }
|
|
expect(manifest.dependencies).toStrictEqual(expectedSpecs)
|
|
project.has('pkg')
|
|
|
|
rimraf('node_modules')
|
|
|
|
await install(manifest, testDefaults({ frozenLockfile: true }))
|
|
project.has('pkg')
|
|
})
|
|
|
|
test('local file via link:', async () => {
|
|
const project = prepareEmpty()
|
|
f.copy('local-pkg', path.resolve('..', 'local-pkg'))
|
|
|
|
const { updatedManifest: manifest } = await addDependenciesToPackage({}, ['link:../local-pkg'], testDefaults())
|
|
|
|
const expectedSpecs = { 'local-pkg': `link:..${path.sep}local-pkg` }
|
|
expect(manifest.dependencies).toStrictEqual(expectedSpecs)
|
|
|
|
const m = project.requireModule('local-pkg')
|
|
|
|
expect(m).toBeTruthy()
|
|
|
|
const lockfile = project.readLockfile()
|
|
|
|
expect(lockfile).toStrictEqual({
|
|
settings: {
|
|
autoInstallPeers: true,
|
|
excludeLinksFromLockfile: false,
|
|
},
|
|
importers: {
|
|
'.': {
|
|
dependencies: {
|
|
'local-pkg': {
|
|
specifier: expectedSpecs['local-pkg'],
|
|
version: 'link:../local-pkg',
|
|
},
|
|
},
|
|
},
|
|
},
|
|
lockfileVersion: LOCKFILE_VERSION,
|
|
})
|
|
})
|
|
|
|
test('local file with symlinked node_modules', async () => {
|
|
const project = prepareEmpty()
|
|
f.copy('local-pkg', path.resolve('..', 'local-pkg'))
|
|
fs.mkdirSync(path.join('..', 'node_modules'))
|
|
await symlinkDir(path.join('..', 'node_modules'), 'node_modules')
|
|
|
|
const { updatedManifest: manifest } = await addDependenciesToPackage({}, ['link:../local-pkg'], testDefaults())
|
|
|
|
const expectedSpecs = { 'local-pkg': `link:..${path.sep}local-pkg` }
|
|
expect(manifest.dependencies).toStrictEqual(expectedSpecs)
|
|
|
|
const m = project.requireModule('local-pkg')
|
|
|
|
expect(m).toBeTruthy()
|
|
|
|
const lockfile = project.readLockfile()
|
|
|
|
expect(lockfile).toStrictEqual({
|
|
settings: {
|
|
autoInstallPeers: true,
|
|
excludeLinksFromLockfile: false,
|
|
},
|
|
importers: {
|
|
'.': {
|
|
dependencies: {
|
|
'local-pkg': {
|
|
specifier: expectedSpecs['local-pkg'],
|
|
version: 'link:../local-pkg',
|
|
},
|
|
},
|
|
},
|
|
},
|
|
lockfileVersion: LOCKFILE_VERSION,
|
|
})
|
|
})
|
|
|
|
test('package with a broken symlink', async () => {
|
|
const project = prepareEmpty()
|
|
await addDependenciesToPackage({}, [f.find('has-broken-symlink.tar.gz')], testDefaults({ fastUnpack: false }))
|
|
|
|
const m = project.requireModule('has-broken-symlink')
|
|
|
|
expect(m).toBeTruthy()
|
|
})
|
|
|
|
test('tarball local package', async () => {
|
|
const project = prepareEmpty()
|
|
const { updatedManifest: manifest } = await addDependenciesToPackage({}, [f.find('tar-pkg-1.0.0.tgz')], testDefaults({ fastUnpack: false }))
|
|
|
|
const m = project.requireModule('tar-pkg')
|
|
|
|
expect(m()).toBe('tar-pkg')
|
|
|
|
const pkgSpec = `file:${normalizePath(f.find('tar-pkg-1.0.0.tgz'))}`
|
|
expect(manifest.dependencies).toStrictEqual({ 'tar-pkg': pkgSpec })
|
|
|
|
const lockfile = project.readLockfile()
|
|
expect(lockfile.packages[`tar-pkg@${lockfile.importers['.'].dependencies!['tar-pkg'].version}`]).toStrictEqual({
|
|
resolution: {
|
|
integrity: 'sha512-HP/5Rgt3pVFLzjmN9qJJ6vZMgCwoCIl/m2bPndYT283CUqnmFiMx0GeeIJ7SyK6TYoJM78SEvFEOQie++caHqw==',
|
|
tarball: `file:${normalizePath(path.relative(process.cwd(), f.find('tar-pkg-1.0.0.tgz')))}`,
|
|
},
|
|
version: '1.0.0',
|
|
})
|
|
})
|
|
|
|
test('tarball local package from project directory', async () => {
|
|
const project = prepareEmpty()
|
|
|
|
f.copy('tar-pkg-1.0.0.tgz', path.resolve('tar-pkg-1.0.0.tgz'))
|
|
|
|
const { updatedManifest: manifest } = await install({
|
|
dependencies: {
|
|
'tar-pkg': 'file:tar-pkg-1.0.0.tgz',
|
|
},
|
|
}, testDefaults({ fastUnpack: false }))
|
|
|
|
const m = project.requireModule('tar-pkg')
|
|
|
|
expect(m()).toBe('tar-pkg')
|
|
|
|
const pkgSpec = 'file:tar-pkg-1.0.0.tgz'
|
|
expect(manifest.dependencies).toStrictEqual({ 'tar-pkg': pkgSpec })
|
|
|
|
const lockfile = project.readLockfile()
|
|
expect(lockfile.importers['.'].dependencies?.['tar-pkg'].version).toBe(pkgSpec)
|
|
expect(lockfile.packages[`tar-pkg@${lockfile.importers['.'].dependencies!['tar-pkg'].version}`]).toStrictEqual({
|
|
resolution: {
|
|
integrity: 'sha512-HP/5Rgt3pVFLzjmN9qJJ6vZMgCwoCIl/m2bPndYT283CUqnmFiMx0GeeIJ7SyK6TYoJM78SEvFEOQie++caHqw==',
|
|
tarball: pkgSpec,
|
|
},
|
|
version: '1.0.0',
|
|
})
|
|
})
|
|
|
|
test('update tarball local package when its integrity changes', async () => {
|
|
const project = prepareEmpty()
|
|
|
|
f.copy('tar-pkg-with-dep-1/tar-pkg-with-dep-1.0.0.tgz', path.resolve('..', 'tar.tgz'))
|
|
const { updatedManifest: manifest } = await addDependenciesToPackage({}, ['../tar.tgz'], testDefaults())
|
|
|
|
const lockfile1 = project.readLockfile()
|
|
expect(lockfile1.snapshots['tar-pkg-with-dep@file:../tar.tgz'].dependencies!['is-positive']).toBe('1.0.0')
|
|
|
|
f.copy('tar-pkg-with-dep-2/tar-pkg-with-dep-1.0.0.tgz', path.resolve('..', 'tar.tgz'))
|
|
await install(manifest, testDefaults())
|
|
|
|
const lockfile2 = project.readLockfile()
|
|
expect(lockfile2.snapshots['tar-pkg-with-dep@file:../tar.tgz'].dependencies!['is-positive']).toBe('2.0.0')
|
|
|
|
const { default: manifestOfTarballDep } = await import(path.resolve('node_modules/tar-pkg-with-dep/package.json'))
|
|
expect(manifestOfTarballDep.dependencies['is-positive']).toBe('^2.0.0')
|
|
})
|
|
|
|
// Similar to the test above, but for a filtered install.
|
|
// Regression test for https://github.com/pnpm/pnpm/pull/9805.
|
|
test('update tarball local package when its integrity changes (filtered install)', async () => {
|
|
const rootProject = prepareEmpty()
|
|
const lockfileDir = rootProject.dir()
|
|
|
|
const manifests = {
|
|
project1: {
|
|
name: 'project1',
|
|
},
|
|
project2: {
|
|
name: 'project2',
|
|
},
|
|
}
|
|
preparePackages(Object.values(manifests), { tempDir: lockfileDir })
|
|
const allProjects: ProjectOptions[] = Object.entries(manifests)
|
|
.map(([id, manifest]) => ({
|
|
buildIndex: 0,
|
|
manifest,
|
|
rootDir: path.join(lockfileDir, id) as ProjectRootDir,
|
|
}))
|
|
|
|
const options = {
|
|
...testDefaults({
|
|
allProjects,
|
|
}),
|
|
lockfileDir,
|
|
}
|
|
|
|
f.copy('tar-pkg-with-dep-1/tar-pkg-with-dep-1.0.0.tgz', path.resolve('.', 'tar.tgz'))
|
|
await addDependenciesToPackage(
|
|
manifests['project1'],
|
|
['../tar.tgz'],
|
|
{
|
|
...options,
|
|
dir: path.join(options.lockfileDir, 'project1'),
|
|
})
|
|
|
|
const manifestOfTarballDep1 = JSON.parse(fs.readFileSync('project1/node_modules/tar-pkg-with-dep/package.json').toString())
|
|
expect(manifestOfTarballDep1.dependencies['is-positive']).toBe('^1.0.0')
|
|
|
|
f.copy('tar-pkg-with-dep-2/tar-pkg-with-dep-1.0.0.tgz', path.resolve('.', 'tar.tgz'))
|
|
|
|
// Re-initialize the store controller that's created within the testDefaults()
|
|
// function. Otherwise the fetchingLocker will contain results from a prior
|
|
// installation and skip store fetches for the same package ID.
|
|
const nextOptions = {
|
|
...options,
|
|
...testDefaults(allProjects),
|
|
}
|
|
const project1InstallOptions: MutatedProject = {
|
|
mutation: 'install',
|
|
rootDir: path.join(lockfileDir, 'project1') as ProjectRootDir,
|
|
}
|
|
await mutateModules([project1InstallOptions], nextOptions)
|
|
|
|
const manifestOfTarballDep2 = JSON.parse(fs.readFileSync('project1/node_modules/tar-pkg-with-dep/package.json').toString())
|
|
expect(manifestOfTarballDep2.dependencies['is-positive']).toBe('^2.0.0')
|
|
})
|
|
|
|
// Covers https://github.com/pnpm/pnpm/issues/1878
|
|
test('do not update deps when installing in a project that has local tarball dep', async () => {
|
|
await addDistTag({ package: '@pnpm.e2e/peer-a', version: '1.0.0', distTag: 'latest' })
|
|
const project = prepareEmpty()
|
|
|
|
f.copy('tar-pkg-with-dep-1/tar-pkg-with-dep-1.0.0.tgz', path.resolve('..', 'tar.tgz'))
|
|
const { updatedManifest: manifest } = await addDependenciesToPackage({}, ['../tar.tgz', '@pnpm.e2e/peer-a'], testDefaults({ lockfileOnly: true }))
|
|
|
|
const initialLockfile = project.readLockfile()
|
|
|
|
await addDistTag({ package: '@pnpm.e2e/peer-a', version: '1.0.1', distTag: 'latest' })
|
|
|
|
await mutateModulesInSingleProject({
|
|
manifest,
|
|
mutation: 'install',
|
|
rootDir: process.cwd() as ProjectRootDir,
|
|
}, testDefaults())
|
|
|
|
const latestLockfile = project.readLockfile()
|
|
|
|
expect(initialLockfile).toStrictEqual(latestLockfile)
|
|
})
|
|
|
|
// Covers https://github.com/pnpm/pnpm/issues/1882
|
|
test('frozen-lockfile: installation fails if the integrity of a tarball dependency changed', async () => {
|
|
prepareEmpty()
|
|
|
|
f.copy('tar-pkg-with-dep-1/tar-pkg-with-dep-1.0.0.tgz', path.resolve('..', 'tar.tgz'))
|
|
const { updatedManifest: manifest } = await addDependenciesToPackage({}, ['../tar.tgz'], testDefaults())
|
|
|
|
rimraf('node_modules')
|
|
|
|
f.copy('tar-pkg-with-dep-2/tar-pkg-with-dep-1.0.0.tgz', path.resolve('..', 'tar.tgz'))
|
|
|
|
await expect(
|
|
install(manifest, testDefaults({ frozenLockfile: true }))
|
|
).rejects.toThrow(/Got unexpected checksum/)
|
|
})
|
|
|
|
test('deep local', async () => {
|
|
const manifest1 = {
|
|
name: 'project-1',
|
|
version: '1.0.0',
|
|
dependencies: {
|
|
'project-2': 'file:../project-2',
|
|
},
|
|
}
|
|
preparePackages([
|
|
{
|
|
location: 'project-1',
|
|
package: manifest1,
|
|
},
|
|
{
|
|
location: 'project-2',
|
|
package: {
|
|
name: 'project-2',
|
|
version: '1.0.0',
|
|
dependencies: {
|
|
'project-3': 'file:./project-3',
|
|
},
|
|
},
|
|
},
|
|
{
|
|
location: 'project-2/project-3',
|
|
package: {
|
|
name: 'project-3',
|
|
version: '1.0.0',
|
|
},
|
|
},
|
|
])
|
|
process.chdir('project-1')
|
|
await install(manifest1, testDefaults())
|
|
|
|
const lockfile = readYamlFile<LockfileFile>('pnpm-lock.yaml')
|
|
expect(Object.keys(lockfile.packages ?? {})).toStrictEqual(['project-2@file:../project-2', 'project-3@file:../project-2/project-3'])
|
|
})
|
|
|
|
// Covers https://github.com/pnpm/pnpm/issues/5327
|
|
test('resolution should not fail when a peer is resolved from a local package and there are many circular dependencies', async () => {
|
|
const manifest1 = {
|
|
name: 'chained-iterator',
|
|
version: '0.0.4',
|
|
dependencies: {
|
|
'@bryntum/siesta': '6.0.0-beta-1',
|
|
},
|
|
}
|
|
const manifest2 = {
|
|
name: '@bryntum/chronograph',
|
|
version: '2.0.3',
|
|
dependencies: {
|
|
'@bryntum/siesta': '6.0.0-beta-1',
|
|
'typescript-serializable-mixin': '0.0.3',
|
|
'typescript-mixin-class': 'link:../typescript-mixin-class',
|
|
},
|
|
}
|
|
const manifest3 = {
|
|
name: 'typescript-mixin-class',
|
|
version: '0.0.3',
|
|
dependencies: {
|
|
'@bryntum/siesta': '6.0.0-beta-1',
|
|
},
|
|
}
|
|
|
|
preparePackages([
|
|
{
|
|
location: manifest1.name,
|
|
package: manifest1,
|
|
},
|
|
{
|
|
location: manifest2.name,
|
|
package: manifest2,
|
|
},
|
|
{
|
|
location: manifest3.name,
|
|
package: manifest3,
|
|
},
|
|
])
|
|
const importers: MutatedProject[] = [
|
|
{
|
|
mutation: 'install',
|
|
rootDir: path.resolve(manifest1.name) as ProjectRootDir,
|
|
},
|
|
{
|
|
mutation: 'install',
|
|
rootDir: path.resolve(manifest2.name) as ProjectRootDir,
|
|
},
|
|
{
|
|
mutation: 'install',
|
|
rootDir: path.resolve(manifest3.name) as ProjectRootDir,
|
|
},
|
|
]
|
|
const allProjects = [
|
|
{
|
|
buildIndex: 0,
|
|
manifest: manifest1,
|
|
rootDir: path.resolve(manifest1.name) as ProjectRootDir,
|
|
},
|
|
{
|
|
buildIndex: 0,
|
|
manifest: manifest2,
|
|
rootDir: path.resolve(manifest2.name) as ProjectRootDir,
|
|
},
|
|
{
|
|
buildIndex: 0,
|
|
manifest: manifest3,
|
|
rootDir: path.resolve(manifest3.name) as ProjectRootDir,
|
|
},
|
|
]
|
|
await mutateModules(importers, testDefaults({ allProjects, lockfileOnly: true, strictPeerDependencies: false }))
|
|
// All we need to know in this test is that installation doesn't fail
|
|
})
|
|
|
|
test('re-install should update local file dependency', async () => {
|
|
const project = prepareEmpty()
|
|
f.copy('local-pkg', path.resolve('..', 'local-pkg'))
|
|
|
|
const { updatedManifest: manifest } = await addDependenciesToPackage({}, ['file:../local-pkg'], testDefaults())
|
|
|
|
const expectedSpecs = { 'local-pkg': `file:..${path.sep}local-pkg` }
|
|
expect(manifest.dependencies).toStrictEqual(expectedSpecs)
|
|
|
|
const m = project.requireModule('local-pkg')
|
|
|
|
expect(m).toBeTruthy()
|
|
expect(fs.existsSync('./node_modules/local-pkg/add.js')).toBeFalsy()
|
|
|
|
let lockfile = project.readLockfile()
|
|
|
|
expect(lockfile).toStrictEqual({
|
|
settings: {
|
|
autoInstallPeers: true,
|
|
excludeLinksFromLockfile: false,
|
|
},
|
|
importers: {
|
|
'.': {
|
|
dependencies: {
|
|
'local-pkg': {
|
|
specifier: expectedSpecs['local-pkg'],
|
|
version: 'file:../local-pkg',
|
|
},
|
|
},
|
|
},
|
|
},
|
|
packages: {
|
|
'local-pkg@file:../local-pkg': {
|
|
resolution: { directory: '../local-pkg', type: 'directory' },
|
|
},
|
|
},
|
|
snapshots: {
|
|
'local-pkg@file:../local-pkg': {},
|
|
},
|
|
lockfileVersion: LOCKFILE_VERSION,
|
|
})
|
|
|
|
// add file
|
|
fs.writeFileSync('../local-pkg/add.js', 'added', 'utf8')
|
|
await install(manifest, testDefaults())
|
|
expect(fs.existsSync('./node_modules/local-pkg/add.js')).toBeTruthy()
|
|
|
|
// remove file
|
|
fs.rmSync('../local-pkg/add.js')
|
|
await install(manifest, testDefaults())
|
|
expect(fs.existsSync('./node_modules/local-pkg/add.js')).toBeFalsy()
|
|
|
|
// add dependency
|
|
expect(fs.existsSync('./node_modules/.pnpm/is-positive@1.0.0')).toBeFalsy()
|
|
fs.writeFileSync('../local-pkg/package.json', JSON.stringify({
|
|
name: 'local-pkg',
|
|
version: '1.0.0',
|
|
dependencies: {
|
|
'is-positive': '1.0.0',
|
|
},
|
|
}), 'utf8')
|
|
await install(manifest, testDefaults())
|
|
expect(fs.existsSync('./node_modules/.pnpm/is-positive@1.0.0')).toBeTruthy()
|
|
lockfile = project.readLockfile()
|
|
expect(lockfile).toMatchObject({
|
|
snapshots: {
|
|
'local-pkg@file:../local-pkg': {
|
|
dependencies: {
|
|
'is-positive': '1.0.0',
|
|
},
|
|
},
|
|
},
|
|
packages: {
|
|
'local-pkg@file:../local-pkg': {
|
|
resolution: { directory: '../local-pkg', type: 'directory' },
|
|
},
|
|
},
|
|
})
|
|
|
|
// update dependency
|
|
fs.writeFileSync('../local-pkg/package.json', JSON.stringify({
|
|
name: 'local-pkg',
|
|
version: '1.0.0',
|
|
dependencies: {
|
|
'is-positive': '2.0.0',
|
|
},
|
|
}), 'utf8')
|
|
await install(manifest, testDefaults())
|
|
expect(fs.existsSync('./node_modules/.pnpm/is-positive@2.0.0')).toBeTruthy()
|
|
lockfile = project.readLockfile()
|
|
expect(lockfile).toMatchObject({
|
|
packages: {
|
|
'local-pkg@file:../local-pkg': {
|
|
resolution: { directory: '../local-pkg', type: 'directory' },
|
|
},
|
|
},
|
|
snapshots: {
|
|
'local-pkg@file:../local-pkg': {
|
|
dependencies: {
|
|
'is-positive': '2.0.0',
|
|
},
|
|
},
|
|
},
|
|
lockfileVersion: LOCKFILE_VERSION,
|
|
})
|
|
})
|
|
|
|
test('local directory is not relinked if disableRelinkLocalDirDeps is set to true', async () => {
|
|
prepareEmpty()
|
|
fs.mkdirSync('pkg')
|
|
fs.writeFileSync('pkg/index.js', 'hello', 'utf8')
|
|
fs.writeFileSync('pkg/package.json', '{"name": "pkg"}', 'utf8')
|
|
|
|
const { updatedManifest: manifest } = await addDependenciesToPackage({}, ['file:./pkg'], testDefaults())
|
|
|
|
fs.writeFileSync('pkg/new.js', 'hello', 'utf8')
|
|
|
|
await addDependenciesToPackage(manifest, ['is-odd@1.0.0'], testDefaults({ disableRelinkLocalDirDeps: true }))
|
|
|
|
expect(fs.readdirSync('node_modules/pkg').sort()).toStrictEqual(['index.js', 'package.json'])
|
|
|
|
await install(manifest, testDefaults({ frozenLockfile: true, disableRelinkLocalDirDeps: true }))
|
|
|
|
expect(fs.readdirSync('node_modules/pkg').sort()).toStrictEqual(['index.js', 'package.json'])
|
|
})
|