diff --git a/.changeset/tricky-coats-join.md b/.changeset/tricky-coats-join.md new file mode 100644 index 00000000000..bb9ca5f34d3 --- /dev/null +++ b/.changeset/tricky-coats-join.md @@ -0,0 +1,6 @@ +--- +"@pnpm/lifecycle": patch +"pnpm": patch +--- + +Throw an error while missing script start or file `server.js` [#5782](https://github.com/pnpm/pnpm/pull/5782). diff --git a/exec/lifecycle/package.json b/exec/lifecycle/package.json index f903a83a2ff..721d098fa4c 100644 --- a/exec/lifecycle/package.json +++ b/exec/lifecycle/package.json @@ -37,6 +37,7 @@ "dependencies": { "@pnpm/core-loggers": "workspace:*", "@pnpm/directory-fetcher": "workspace:*", + "@pnpm/error": "workspace:*", "@pnpm/npm-lifecycle": "^2.0.0", "@pnpm/read-package-json": "workspace:*", "@pnpm/store-controller-types": "workspace:*", @@ -46,6 +47,7 @@ }, "devDependencies": { "@pnpm/lifecycle": "workspace:*", + "@pnpm/test-fixtures": "workspace:*", "@types/rimraf": "^3.0.2", "@zkochan/rimraf": "^2.1.2", "json-append": "1.1.1", diff --git a/exec/lifecycle/src/runLifecycleHook.ts b/exec/lifecycle/src/runLifecycleHook.ts index cde86cee426..e24a0ecc903 100644 --- a/exec/lifecycle/src/runLifecycleHook.ts +++ b/exec/lifecycle/src/runLifecycleHook.ts @@ -2,6 +2,8 @@ import { lifecycleLogger } from '@pnpm/core-loggers' import { globalWarn } from '@pnpm/logger' import lifecycle from '@pnpm/npm-lifecycle' import { DependencyManifest, ProjectManifest } from '@pnpm/types' +import { PnpmError } from '@pnpm/error' +import { existsSync } from 'fs' function noop () {} // eslint-disable-line:no-empty @@ -34,6 +36,9 @@ export async function runLifecycleHook ( m.scripts = { ...m.scripts } if (stage === 'start' && !m.scripts.start) { + if (!existsSync('server.js')) { + throw new PnpmError('NO_SCRIPT_OR_SERVER', 'Missing script start or file server.js') + } m.scripts.start = 'node server.js' } if (opts.args?.length && m.scripts?.[stage]) { diff --git a/exec/lifecycle/test/fixtures/without-scriptstart-serverjs/package.json b/exec/lifecycle/test/fixtures/without-scriptstart-serverjs/package.json new file mode 100644 index 00000000000..3debaddf0bb --- /dev/null +++ b/exec/lifecycle/test/fixtures/without-scriptstart-serverjs/package.json @@ -0,0 +1,5 @@ +{ + "name": "without-scriptstart-serverjs", + "version": "1.0.0", + "scripts": {} +} diff --git a/exec/lifecycle/test/index.ts b/exec/lifecycle/test/index.ts index d407cfd0c45..0bd83ac0516 100644 --- a/exec/lifecycle/test/index.ts +++ b/exec/lifecycle/test/index.ts @@ -3,12 +3,14 @@ import path from 'path' import { runLifecycleHook, runPostinstallHooks } from '@pnpm/lifecycle' import loadJsonFile from 'load-json-file' import rimraf from '@zkochan/rimraf' +import { PnpmError } from '@pnpm/error' +import { fixtures } from '@pnpm/test-fixtures' -const fixtures = path.join(__dirname, 'fixtures') +const f = fixtures(path.join(__dirname, 'fixtures')) const rootModulesDir = path.join(__dirname, '..', 'node_modules') test('runLifecycleHook()', async () => { - const pkgRoot = path.join(fixtures, 'simple') + const pkgRoot = f.find('simple') const pkg = await import(path.join(pkgRoot, 'package.json')) await runLifecycleHook('postinstall', pkg, { depPath: '/simple/1.0.0', @@ -23,7 +25,7 @@ test('runLifecycleHook()', async () => { }) test('runLifecycleHook() escapes the args passed to the script', async () => { - const pkgRoot = path.join(fixtures, 'escape-args') + const pkgRoot = f.find('escape-args') const pkg = await import(path.join(pkgRoot, 'package.json')) await runLifecycleHook('echo', pkg, { depPath: '/escape-args/1.0.0', @@ -38,7 +40,7 @@ test('runLifecycleHook() escapes the args passed to the script', async () => { }) test('runPostinstallHooks()', async () => { - const pkgRoot = path.join(fixtures, 'with-many-scripts') + const pkgRoot = f.find('with-many-scripts') await rimraf(path.join(pkgRoot, 'output.json')) await runPostinstallHooks({ depPath: '/with-many-scripts/1.0.0', @@ -51,3 +53,18 @@ test('runPostinstallHooks()', async () => { expect(loadJsonFile.sync(path.join(pkgRoot, 'output.json'))).toStrictEqual(['preinstall', 'install', 'postinstall']) }) + +test('runLifecycleHook() should throw an error while missing script start or file server.js', async () => { + const pkgRoot = f.find('without-scriptstart-serverjs') + const pkg = await import(path.join(pkgRoot, 'package.json')) + await expect( + runLifecycleHook('start', pkg, { + depPath: '/without-scriptstart-serverjs/1.0.0', + optional: false, + pkgRoot, + rawConfig: {}, + rootModulesDir, + unsafePerm: true, + }) + ).rejects.toThrow(new PnpmError('NO_SCRIPT_OR_SERVER', 'Missing script start or file server.js')) +}) diff --git a/exec/lifecycle/tsconfig.json b/exec/lifecycle/tsconfig.json index cc2eff73b45..cd15192e13f 100644 --- a/exec/lifecycle/tsconfig.json +++ b/exec/lifecycle/tsconfig.json @@ -9,12 +9,18 @@ "../../__typings__/**/*.d.ts" ], "references": [ + { + "path": "../../__utils__/test-fixtures" + }, { "path": "../../fetching/directory-fetcher" }, { "path": "../../packages/core-loggers" }, + { + "path": "../../packages/error" + }, { "path": "../../packages/types" }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 240cd23c891..4e0c7a6e796 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -903,6 +903,9 @@ importers: '@pnpm/directory-fetcher': specifier: workspace:* version: link:../../fetching/directory-fetcher + '@pnpm/error': + specifier: workspace:* + version: link:../../packages/error '@pnpm/logger': specifier: ^5.0.0 version: 5.0.0 @@ -928,6 +931,9 @@ importers: '@pnpm/lifecycle': specifier: workspace:* version: 'link:' + '@pnpm/test-fixtures': + specifier: workspace:* + version: link:../../__utils__/test-fixtures '@types/rimraf': specifier: ^3.0.2 version: 3.0.2