Skip to content

@pnpm/lockfile-types is outdated #4517

Description

@milahu

actual version: 4
expected version: 5

latest version of pnpm produces lockfileVersion: 5.3

the typescript types in @pnpm/lockfile-types are not (yet) used in pnpm,
because lockfiles exist only on runtime (after the typescript build step),
that's why it's not a bug for pnpm

the types are useful to write parsers for pnpm-lock.yaml
for example snyk-nodejs-lockfile-parser

failing test:
validate pnpm-lock.yaml files against types in @pnpm/lockfile-types

validate-lockfile.mjs
// pnpm/packages/pnpm/test/install/validate-lockfile.mjs
// pnpm install -D ts-json-schema-generator ajv

import path from 'path'
import fs from 'fs'
import { createRequire } from 'module';
const require = createRequire(import.meta.url);

import * as Tsj from 'ts-json-schema-generator'
//import * as Tjs_broken from 'typescript-json-schema' // -> invalid schema
const use_Tjs_broken = false;
//const use_Tjs_broken = true; // -> invalid schema

import Ajv from "ajv"
//import addFormats from 'ajv-formats' // addFormats(ajv)

import readYamlFile from 'read-yaml-file'
/*import { Lockfile } from '@pnpm/lockfile-types'*/

(async () => {

  //const lockfilePath = '/tmp/pnpm-lock.yaml'
  const lockfilePath = path.resolve(path.dirname(process.argv[1]), '../../../../pnpm-lock.yaml'); // workspace lockfile
  console.log(`lockfilePath = ${lockfilePath}`);
  const lockfile = await readYamlFile/*<Lockfile>*/(lockfilePath)
  //console.dir(lockfile);

  const lockfileVersionMajor = lockfile.lockfileVersion | 0;

  const { version: lockfileTypesVersion } = JSON.parse(fs.readFileSync(require.resolve('@pnpm/lockfile-types/package.json')));
  const lockfileTypesVersionMajor = parseInt(lockfileTypesVersion.split('.')[0]);

  if (lockfileVersionMajor != lockfileTypesVersionMajor) {
    //throw new Error(
    console.log(
      `version mismatch in lockfileVersion. expected ${lockfileTypesVersionMajor}, actual ${lockfileVersionMajor}`
    );
  }

  const schema = (() => {

    if (fs.existsSync("pnpm-lockfile.schema.json")) {
      console.log("using cached pnpm-lockfile.schema.json");
      const json = fs.readFileSync("pnpm-lockfile.schema.json", "utf8");
      return JSON.parse(json);
    }

    console.log("generating pnpm-lockfile.schema.json from @pnpm/lockfile-types ... this may take a minute");

    //const lockfileTypesPath = require.resolve("@pnpm/lockfile-types/lib/index.d.ts")
    const lockfileTypesPath = require.resolve("@pnpm/lockfile-types/lib/index.d.ts")
    //console.log(lockfileTypesPath)

    let schema;

    if (use_Tjs_broken == false) {
      // https://github.com/vega/ts-json-schema-generator#programmatic-usage

      const config = {
        path: require.resolve("@pnpm/lockfile-types/lib/index.d.ts"),
        tsconfig: require.resolve("@pnpm/lockfile-types/tsconfig.json"),
        type: "Lockfile", // * or <type-name> if you want to generate schema for that one type only
      };
      var t1 = performance.now();
      schema = Tsj.createGenerator(config).createSchema(config.type);
      var t2 = performance.now();
      var dt = (t2 - t1) / 1000;
      console.log(`schema was generated in ${dt} seconds`) // 80 seconds
    }

    else {
      schema = getSchemaTjs_broken();
    }

    fs.writeFileSync("pnpm-lockfile.schema.json", JSON.stringify(schema, null, 2), "utf8");
    console.log("done pnpm-lockfile.schema.json")

    return schema;
  })();

  console.log('new Ajv')
  const ajv = new Ajv({
    strict: true,
    allErrors: true,
  })

  console.log('ajv.validateSchema')
  ajv.validateSchema(schema);
  if (ajv.errors) {
    console.dir(ajv.errors, { depth: null });
    throw new Error("The schema is not valid");
  }

  console.log('ajv.compile')
  const validate = ajv.compile(schema)

  console.log('validate')
  const valid = validate(lockfile)

  if (!valid) console.log(validate.errors)

})()



function getSchemaTjs_broken() {

  // https://github.com/YousefED/typescript-json-schema/blob/master/test/schema.test.ts

  // optionally pass argument to schema generator
  const settings /*: Tjs_broken.PartialArgs*/ = {
    //required: true,
  };

  // optionally pass ts compiler options
  const compilerOptions/*: Tjs_broken.CompilerOptions*/ = {
    //strictNullChecks: true,
  };

  // optionally pass a base path
  //const basePath = "./my-dir";

  //../lockfile-types/lib/index.d.ts
  /*
  console.log(path.resolve("@pnpm/lockfile-types/lib/index.d.ts"))
  console.log(path.resolve("../../../lockfile-types/lib/index.d.ts"))
  */

  console.log('Tjs_broken.getProgramFromFiles')
  const program = Tjs_broken.getProgramFromFiles(
    [lockfileTypesPath],
    compilerOptions,
    //basePath
  );

  // We can either get the schema for one file and one type...
  // TODO cache schema. Tjs_broken.generateSchema is slow
  console.log('Tjs_broken.generateSchema')
  const schema = Tjs_broken.generateSchema(program, "Lockfile", settings);
  return schema;
}

side note:
would be nice to have a npm package like @pnpm/lockfile-schemas
with compiled schemas for different lockfile versions
generating the schema takes about one minute
one schema file has 8 KByte with pretty json

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions