UNPKG

@yarnpkg/parsers

Version:
133 lines (132 loc) 5.23 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.PreserveOrdering = void 0; exports.stringifySyml = stringifySyml; exports.parseSyml = parseSyml; const js_yaml_1 = require("js-yaml"); const syml_1 = require("./grammars/syml"); const simpleStringPattern = /^(?![-?:,\][{}#&*!|>'"%@` \t\r\n]).([ \t]*(?![,\][{}:# \t\r\n]).)*$/; // The following keys will always be stored at the top of the object, in the // specified order. It's not fair but life isn't fair either. const specialObjectKeys = [`__metadata`, `version`, `resolution`, `dependencies`, `peerDependencies`, `dependenciesMeta`, `peerDependenciesMeta`, `binaries`]; class PreserveOrdering { constructor(data) { this.data = data; } } exports.PreserveOrdering = PreserveOrdering; function stringifyString(value) { if (value.match(simpleStringPattern)) { return value; } else { return JSON.stringify(value); } } function isRemovableField(value) { if (typeof value === `undefined`) return true; if (typeof value === `object` && value !== null && !Array.isArray(value)) return Object.keys(value).every(key => isRemovableField(value[key])); return false; } function stringifyValue(value, indentLevel, newLineIfObject) { if (value === null) return `null\n`; if (typeof value === `number` || typeof value === `boolean`) return `${value.toString()}\n`; if (typeof value === `string`) return `${stringifyString(value)}\n`; if (Array.isArray(value)) { if (value.length === 0) return `[]\n`; const indent = ` `.repeat(indentLevel); const serialized = value.map(sub => { return `${indent}- ${stringifyValue(sub, indentLevel + 1, false)}`; }).join(``); return `\n${serialized}`; } if (typeof value === `object` && value) { const [data, sort] = value instanceof PreserveOrdering ? [value.data, false] : [value, true]; const indent = ` `.repeat(indentLevel); const keys = Object.keys(data); if (sort) { keys.sort((a, b) => { const aIndex = specialObjectKeys.indexOf(a); const bIndex = specialObjectKeys.indexOf(b); if (aIndex === -1 && bIndex === -1) return a < b ? -1 : a > b ? +1 : 0; if (aIndex !== -1 && bIndex === -1) return -1; if (aIndex === -1 && bIndex !== -1) return +1; return aIndex - bIndex; }); } const fields = keys.filter(key => { return !isRemovableField(data[key]); }).map((key, index) => { const value = data[key]; const stringifiedKey = stringifyString(key); const stringifiedValue = stringifyValue(value, indentLevel + 1, true); const recordIndentation = index > 0 || newLineIfObject ? indent : ``; // Yaml 1.2 spec says that keys over 1024 characters need to be prefixed with ? and the : goes in a new line const keyPart = stringifiedKey.length > 1024 ? `? ${stringifiedKey}\n${recordIndentation}:` : `${stringifiedKey}:`; const valuePart = stringifiedValue.startsWith(`\n`) ? stringifiedValue : ` ${stringifiedValue}`; return `${recordIndentation}${keyPart}${valuePart}`; }).join(indentLevel === 0 ? `\n` : ``) || `\n`; if (!newLineIfObject) { return `${fields}`; } else { return `\n${fields}`; } } throw new Error(`Unsupported value type (${value})`); } function stringifySyml(value) { try { const stringified = stringifyValue(value, 0, false); return stringified !== `\n` ? stringified : ``; } catch (error) { if (error.location) error.message = error.message.replace(/(\.)?$/, ` (line ${error.location.start.line}, column ${error.location.start.column})$1`); throw error; } } stringifySyml.PreserveOrdering = PreserveOrdering; function parseViaPeg(source) { if (!source.endsWith(`\n`)) source += `\n`; return (0, syml_1.parse)(source); } const LEGACY_REGEXP = /^(#.*(\r?\n))*?#\s+yarn\s+lockfile\s+v1\r?\n/i; function parseViaJsYaml(source) { if (LEGACY_REGEXP.test(source)) return parseViaPeg(source); const value = (0, js_yaml_1.safeLoad)(source, { schema: js_yaml_1.FAILSAFE_SCHEMA, json: true, }); // Empty files are parsed as `undefined` instead of an empty object // Empty files with 2 newlines or more are `null` instead if (value === undefined || value === null) return {}; if (typeof value !== `object`) throw new Error(`Expected an indexed object, got a ${typeof value} instead. Does your file follow Yaml's rules?`); if (Array.isArray(value)) throw new Error(`Expected an indexed object, got an array instead. Does your file follow Yaml's rules?`); return value; } function parseSyml(source) { return parseViaJsYaml(source); }