relion 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/CHANGELOG.md +47 -0
  2. package/LICENSE +21 -0
  3. package/README.md +1 -0
  4. package/bin/cli.js +9 -0
  5. package/package.json +74 -0
  6. package/src/commands.js +190 -0
  7. package/src/defaults.js +68 -0
  8. package/src/index.js +152 -0
  9. package/src/lib/checkpoint.js +23 -0
  10. package/src/lib/configuration.js +35 -0
  11. package/src/lib/detect-package-manager.js +49 -0
  12. package/src/lib/format-commit-message.js +4 -0
  13. package/src/lib/latest-semver-tag.js +34 -0
  14. package/src/lib/lifecycles/bump.js +234 -0
  15. package/src/lib/lifecycles/changelog.js +105 -0
  16. package/src/lib/lifecycles/commit.js +67 -0
  17. package/src/lib/lifecycles/tag.js +59 -0
  18. package/src/lib/print-error.js +15 -0
  19. package/src/lib/run-exec.js +19 -0
  20. package/src/lib/run-execFile.js +19 -0
  21. package/src/lib/run-lifecycle-script.js +18 -0
  22. package/src/lib/stringify-package.js +34 -0
  23. package/src/lib/updaters/index.js +127 -0
  24. package/src/lib/updaters/types/csproj.js +13 -0
  25. package/src/lib/updaters/types/gradle.js +16 -0
  26. package/src/lib/updaters/types/json.js +25 -0
  27. package/src/lib/updaters/types/maven.js +43 -0
  28. package/src/lib/updaters/types/openapi.js +15 -0
  29. package/src/lib/updaters/types/plain-text.js +7 -0
  30. package/src/lib/updaters/types/python.js +30 -0
  31. package/src/lib/updaters/types/yaml.js +15 -0
  32. package/src/lib/write-file.js +6 -0
  33. package/src/preset/constants.js +16 -0
  34. package/src/preset/index.js +19 -0
  35. package/src/preset/parser.js +11 -0
  36. package/src/preset/templates/commit.hbs +19 -0
  37. package/src/preset/templates/footer.hbs +10 -0
  38. package/src/preset/templates/header.hbs +10 -0
  39. package/src/preset/templates/index.js +13 -0
  40. package/src/preset/templates/main.hbs +21 -0
  41. package/src/preset/whatBump.js +32 -0
  42. package/src/preset/writer.js +201 -0
@@ -0,0 +1,34 @@
1
+ import * as gitSemverTags from 'git-semver-tags';
2
+ import semver from 'semver';
3
+
4
+ export default function ({ tagPrefix, prerelease }) {
5
+ return new Promise((resolve, reject) => {
6
+ gitSemverTags({ tagPrefix }, function (err, tags) {
7
+ if (err) return reject(err);
8
+ else if (!tags.length) return resolve('1.0.0');
9
+ // Respect tagPrefix
10
+ tags = tags.map((tag) => tag.replace(new RegExp('^' + tagPrefix), ''));
11
+ if (prerelease) {
12
+ // ignore any other prelease tags
13
+ tags = tags.filter((tag) => {
14
+ if (!semver.valid(tag)) return false;
15
+ if (!semver.prerelease(tag)) {
16
+ // include all non-prerelease versions
17
+ return true;
18
+ }
19
+ // check if the name of the prerelease matches the one we are looking for
20
+ if (semver.prerelease(tag)[0] === prerelease) {
21
+ return true;
22
+ }
23
+ return false;
24
+ });
25
+ }
26
+ // ensure that the largest semver tag is at the head.
27
+ tags = tags.map((tag) => {
28
+ return semver.clean(tag);
29
+ });
30
+ tags.sort(semver.rcompare);
31
+ return resolve(tags[0]);
32
+ });
33
+ });
34
+ }
@@ -0,0 +1,234 @@
1
+ import checkpoint from '../checkpoint.js';
2
+ import { Bumper } from 'conventional-recommended-bump';
3
+ import fs from 'fs';
4
+ import DotGitignore from 'dotgitignore';
5
+ import path from 'path';
6
+ import runLifecycleScript from '../run-lifecycle-script.js';
7
+ import semver from 'semver';
8
+ import writeFile from '../write-file.js';
9
+ import { resolveUpdaterObjectFromArgument } from '../updaters/index.js';
10
+ let configsToUpdate = {};
11
+ const sanitizeQuotesRegex = /['"]+/g;
12
+
13
+ async function Bump(args, newVersion) {
14
+ // reset the cache of updated config files each
15
+ // time we perform the version bump step.
16
+ configsToUpdate = {};
17
+
18
+ if (
19
+ args.releaseAs &&
20
+ !(['major', 'minor', 'patch'].includes(args.releaseAs.toLowerCase()) || semver.valid(args.releaseAs))
21
+ ) {
22
+ throw new Error("releaseAs must be one of 'major', 'minor' or 'patch', or a valid semvar version.");
23
+ }
24
+
25
+ await runLifecycleScript(args, 'prerelease');
26
+ const stdout = await runLifecycleScript(args, 'prebump');
27
+ if (stdout?.trim().length) {
28
+ const prebumpString = stdout.trim().replace(sanitizeQuotesRegex, '');
29
+ if (semver.valid(prebumpString)) args.releaseAs = prebumpString;
30
+ }
31
+ updateConfigs(args, newVersion);
32
+ await runLifecycleScript(args, 'postbump');
33
+ return newVersion;
34
+ }
35
+
36
+ Bump.getUpdatedConfigs = function () {
37
+ return configsToUpdate;
38
+ };
39
+
40
+ /**
41
+ * Get the new version number
42
+ * @param args
43
+ * @param version
44
+ * @returns Promise<string>
45
+ */
46
+ export async function getNewVersion(args, version) {
47
+ let newVersion = version;
48
+ if (semver.valid(args.releaseAs)) {
49
+ const releaseAs = new semver.SemVer(args.releaseAs);
50
+ if (
51
+ isString(args.prerelease) &&
52
+ releaseAs.prerelease.length &&
53
+ releaseAs.prerelease.slice(0, -1).join('.') !== args.prerelease
54
+ ) {
55
+ // If both releaseAs and the prerelease identifier are supplied, they must match. The behavior
56
+ // for a mismatch is undefined, so error out instead.
57
+ throw new Error('releaseAs and prerelease have conflicting prerelease identifiers');
58
+ } else if (isString(args.prerelease) && releaseAs.prerelease.length) {
59
+ newVersion = releaseAs.version;
60
+ } else if (isString(args.prerelease)) {
61
+ newVersion = `${releaseAs.major}.${releaseAs.minor}.${releaseAs.patch}-${args.prerelease}.0`;
62
+ } else {
63
+ newVersion = releaseAs.version;
64
+ }
65
+
66
+ // Check if the previous version is the same version and prerelease, and increment if so
67
+ if (
68
+ isString(args.prerelease) &&
69
+ ['prerelease', null].includes(semver.diff(version, newVersion)) &&
70
+ semver.lte(newVersion, version)
71
+ ) {
72
+ newVersion = semver.inc(version, 'prerelease', args.prerelease);
73
+ }
74
+
75
+ // Append any build info from releaseAs
76
+ newVersion = semvarToVersionStr(newVersion, releaseAs.build);
77
+ } else {
78
+ const release = await bumpVersion(args.releaseAs, version, args);
79
+ const releaseType = getReleaseType(args.prerelease, release.releaseType, version);
80
+
81
+ newVersion = semver.inc(version, releaseType, args.prerelease);
82
+ }
83
+ return newVersion;
84
+ }
85
+
86
+ /**
87
+ * Convert a semver object to a full version string including build metadata
88
+ * @param {string} semverVersion The semvar version string
89
+ * @param {string[]} semverBuild An array of the build metadata elements, to be joined with '.'
90
+ * @returns {string}
91
+ */
92
+ function semvarToVersionStr(semverVersion, semverBuild) {
93
+ return [semverVersion, semverBuild.join('.')].filter(Boolean).join('+');
94
+ }
95
+
96
+ function getReleaseType(prerelease, expectedReleaseType, currentVersion) {
97
+ if (isString(prerelease)) {
98
+ if (isInPrerelease(currentVersion)) {
99
+ if (
100
+ shouldContinuePrerelease(currentVersion, expectedReleaseType) ||
101
+ getTypePriority(getCurrentActiveType(currentVersion)) > getTypePriority(expectedReleaseType)
102
+ ) {
103
+ return 'prerelease';
104
+ }
105
+ }
106
+
107
+ return 'pre' + expectedReleaseType;
108
+ } else {
109
+ return expectedReleaseType;
110
+ }
111
+ }
112
+
113
+ function isString(val) {
114
+ return typeof val === 'string';
115
+ }
116
+
117
+ /**
118
+ * if a version is currently in pre-release state,
119
+ * and if it current in-pre-release type is same as expect type,
120
+ * it should continue the pre-release with the same type
121
+ *
122
+ * @param version
123
+ * @param expectType
124
+ * @return {boolean}
125
+ */
126
+ function shouldContinuePrerelease(version, expectType) {
127
+ return getCurrentActiveType(version) === expectType;
128
+ }
129
+
130
+ function isInPrerelease(version) {
131
+ return Array.isArray(semver.prerelease(version));
132
+ }
133
+
134
+ const TypeList = ['major', 'minor', 'patch'].reverse();
135
+
136
+ /**
137
+ * extract the in-pre-release type in target version
138
+ *
139
+ * @param version
140
+ * @return {string}
141
+ */
142
+ function getCurrentActiveType(version) {
143
+ const typelist = TypeList;
144
+ for (let i = 0; i < typelist.length; i++) {
145
+ if (semver[typelist[i]](version)) {
146
+ return typelist[i];
147
+ }
148
+ }
149
+ }
150
+
151
+ /**
152
+ * calculate the priority of release type,
153
+ * major - 2, minor - 1, patch - 0
154
+ *
155
+ * @param type
156
+ * @return {number}
157
+ */
158
+ function getTypePriority(type) {
159
+ return TypeList.indexOf(type);
160
+ }
161
+
162
+ async function bumpVersion(releaseAs, currentVersion, args) {
163
+ if (releaseAs) {
164
+ return {
165
+ releaseType: releaseAs,
166
+ };
167
+ } else {
168
+ const recommendation = await getRecommendedBump(args, currentVersion);
169
+ if (recommendation) {
170
+ return recommendation;
171
+ } else {
172
+ throw new Error('No recommendation found');
173
+ }
174
+ }
175
+ }
176
+
177
+ async function getRecommendedBump(args, currentVersion) {
178
+ const bumper = new Bumper(args.path);
179
+ bumper.loadPreset({
180
+ preMajor: semver.lt(currentVersion, '1.0.0'),
181
+ ...args.preset,
182
+ });
183
+ bumper.tag({
184
+ tagPrefix: args.tagPrefix,
185
+ lernaPackage: args.lernaPackage,
186
+ });
187
+ bumper.commits({}, args.parserOpts);
188
+ const recommendation = await bumper.bump();
189
+ return recommendation;
190
+ }
191
+
192
+ /**
193
+ * attempt to update the version number in provided `bumpFiles`
194
+ * @param args config object
195
+ * @param newVersion version number to update to.
196
+ * @return void
197
+ */
198
+ function updateConfigs(args, newVersion) {
199
+ const dotgit = DotGitignore();
200
+ args.bumpFiles.forEach(async function (bumpFile) {
201
+ const updater = await resolveUpdaterObjectFromArgument(bumpFile);
202
+ if (!updater) {
203
+ return;
204
+ }
205
+ const configPath = path.resolve(process.cwd(), updater.filename);
206
+ try {
207
+ if (dotgit.ignore(updater.filename)) {
208
+ console.debug(`Not updating file '${updater.filename}', as it is ignored in Git`);
209
+ return;
210
+ }
211
+ const stat = fs.lstatSync(configPath);
212
+
213
+ if (!stat.isFile()) {
214
+ console.debug(`Not updating '${updater.filename}', as it is not a file`);
215
+ return;
216
+ }
217
+ const contents = fs.readFileSync(configPath, 'utf8');
218
+ const newContents = updater.updater.writeVersion(contents, newVersion);
219
+ const realNewVersion = updater.updater.readVersion(newContents);
220
+ checkpoint(args, 'bumping version in ' + updater.filename + ' from %s to %s', [
221
+ updater.updater.readVersion(contents),
222
+ realNewVersion,
223
+ ]);
224
+ writeFile(args, configPath, newContents);
225
+ // flag any config files that we modify the version # for
226
+ // as having been updated.
227
+ configsToUpdate[updater.filename] = true;
228
+ } catch (err) {
229
+ if (err.code !== 'ENOENT') console.warn(err.message);
230
+ }
231
+ });
232
+ }
233
+
234
+ export default Bump;
@@ -0,0 +1,105 @@
1
+ import chalk from 'chalk';
2
+ import checkpoint from '../checkpoint.js';
3
+ import conventionalChangelog from 'conventional-changelog';
4
+ import fs from 'fs';
5
+ import runLifecycleScript from '../run-lifecycle-script.js';
6
+ import writeFile from '../write-file.js';
7
+ const START_OF_LAST_RELEASE_PATTERN = /(^#.*?[0-9]+\.[0-9]+\.[0-9]+|<a name=)/m;
8
+
9
+ async function Changelog(args, newVersion) {
10
+ await runLifecycleScript(args, 'prechangelog');
11
+ await outputChangelog(args, newVersion);
12
+ await runLifecycleScript(args, 'postchangelog');
13
+ }
14
+
15
+ Changelog.START_OF_LAST_RELEASE_PATTERN = START_OF_LAST_RELEASE_PATTERN;
16
+
17
+ export default Changelog;
18
+
19
+ /**
20
+ * Front matter is only extracted and therefore retained in final output where Changelog "header" begins with #Changelog,
21
+ * e.g. meets Standard-Version (last release) or relion(current) format
22
+ */
23
+ function extractFrontMatter(oldContent) {
24
+ const headerStart = oldContent.indexOf('# Changelog');
25
+ return headerStart !== -1 || headerStart !== 0
26
+ ? oldContent.substring(0, headerStart)
27
+ : '';
28
+ }
29
+
30
+ /**
31
+ * find the position of the last release and remove header
32
+ */
33
+ function extractChangelogBody(oldContent) {
34
+ const oldContentStart = oldContent.search(START_OF_LAST_RELEASE_PATTERN);
35
+ return oldContentStart !== -1
36
+ ? oldContent.substring(oldContentStart)
37
+ : oldContent;
38
+ }
39
+
40
+ function outputChangelog(args, newVersion) {
41
+ return new Promise((resolve, reject) => {
42
+ createIfMissing(args);
43
+ const header = args.preset.header;
44
+
45
+ const oldContent =
46
+ args.dryRun || args.releaseCount === 0
47
+ ? ''
48
+ : fs.readFileSync(args.infile, 'utf-8');
49
+
50
+ const oldContentBody = extractChangelogBody(oldContent);
51
+
52
+ const changelogFrontMatter = extractFrontMatter(oldContent);
53
+
54
+ let content = '';
55
+ const changelogStream = conventionalChangelog(
56
+ {
57
+ preset: args.preset,
58
+ tagPrefix: args.tagPrefix,
59
+ releaseCount: args.releaseCount,
60
+ ...(args.verbose
61
+ ? {
62
+ debug: console.info.bind(console, 'conventional-recommended-bump'),
63
+ }
64
+ : {}),
65
+ },
66
+ args.context,
67
+ { merges: null, path: args.path, showSignature: false },
68
+ args.parserOpts,
69
+ args.writerOpts,
70
+ ).on('error', function (err) {
71
+ return reject(err);
72
+ });
73
+
74
+ changelogStream.on('data', function (buffer) {
75
+ content += buffer.toString();
76
+ });
77
+
78
+ changelogStream.on('end', function () {
79
+ checkpoint(args, 'outputting changes to %s', [args.infile]);
80
+ if (args.dryRun)
81
+ console.info(`\n---\n${chalk.gray(content.trim())}\n---\n`);
82
+ else
83
+ writeFile(
84
+ args,
85
+ args.infile,
86
+ changelogFrontMatter +
87
+ header +
88
+ (content + oldContentBody).replace(/\n+$/, '\n'),
89
+ );
90
+ return resolve();
91
+ });
92
+ });
93
+ }
94
+
95
+ function createIfMissing(args) {
96
+ try {
97
+ fs.accessSync(args.infile, fs.F_OK);
98
+ } catch (err) {
99
+ if (err.code === 'ENOENT') {
100
+ checkpoint(args, 'created %s', [args.infile]);
101
+ args.outputUnreleased = true;
102
+ writeFile(args, args.infile, '\n');
103
+ }
104
+ }
105
+ }
@@ -0,0 +1,67 @@
1
+ import bump from '../lifecycles/bump.js';
2
+ import checkpoint from '../checkpoint.js';
3
+ import formatCommitMessage from '../format-commit-message.js';
4
+ import path from 'path';
5
+ import runExecFile from '../run-execFile.js';
6
+ import runLifecycleScript from '../run-lifecycle-script.js';
7
+
8
+ export default async function (args, newVersion) {
9
+ const message = await runLifecycleScript(args, 'precommit');
10
+ if (message && message.length) args.preset.releaseCommitMessageFormat = message;
11
+ await execCommit(args, newVersion);
12
+ await runLifecycleScript(args, 'postcommit');
13
+ }
14
+
15
+ async function execCommit(args, newVersion) {
16
+ let msg = 'committing %s';
17
+ let paths = [];
18
+ const verify = args.verify === false || args.n ? ['--no-verify'] : [];
19
+ const sign = args.sign ? ['-S'] : [];
20
+ const signoff = args.signoff ? ['--signoff'] : [];
21
+ const toAdd = [];
22
+
23
+ // only start with a pre-populated paths list when CHANGELOG processing is not skipped
24
+ if (args.changelog) {
25
+ paths = [args.infile];
26
+ toAdd.push(args.infile);
27
+ }
28
+
29
+ // commit any of the config files that we've updated
30
+ // the version # for.
31
+ Object.keys(bump.getUpdatedConfigs()).forEach(function (p) {
32
+ paths.unshift(p);
33
+ toAdd.push(path.relative(process.cwd(), p));
34
+
35
+ // account for multiple files in the output message
36
+ if (paths.length > 1) {
37
+ msg += ' and %s';
38
+ }
39
+ });
40
+
41
+ if (args.commitAll) {
42
+ msg += ' and %s';
43
+ paths.push('all staged files');
44
+ }
45
+
46
+ checkpoint(args, msg, paths);
47
+
48
+ // nothing to do, exit without commit anything
49
+ if (
50
+ !args.commitAll &&
51
+ !args.changelog &&
52
+ !args.bump &&
53
+ toAdd.length === 0
54
+ ) {
55
+ return;
56
+ }
57
+
58
+ await runExecFile(args, 'git', ['add'].concat(toAdd));
59
+ await runExecFile(
60
+ args,
61
+ 'git',
62
+ ['commit'].concat(verify, sign, signoff, args.commitAll ? [] : toAdd, [
63
+ '-m',
64
+ `${formatCommitMessage(args)}`,
65
+ ]),
66
+ );
67
+ }
@@ -0,0 +1,59 @@
1
+ import bump from '../lifecycles/bump.js';
2
+ import chalk from 'chalk';
3
+ import checkpoint from '../checkpoint.js';
4
+ import figures from 'figures';
5
+ import formatCommitMessage from '../format-commit-message.js';
6
+ import runExecFile from '../run-execFile.js';
7
+ import runLifecycleScript from '../run-lifecycle-script.js';
8
+ import { detectPMByLockFile } from '../detect-package-manager.js';
9
+
10
+ export default async function (newVersion, pkgPrivate, args) {
11
+ await runLifecycleScript(args, 'pretag');
12
+ await execTag(newVersion, pkgPrivate, args);
13
+ await runLifecycleScript(args, 'posttag');
14
+ }
15
+
16
+ async function detectPublishHint() {
17
+ const npmClientName = await detectPMByLockFile();
18
+ const publishCommand = 'publish';
19
+ return `${npmClientName} ${publishCommand}`;
20
+ }
21
+
22
+ async function execTag(newVersion, pkgPrivate, args) {
23
+ const tagOption = [];
24
+ if (args.sign) {
25
+ tagOption.push('-s');
26
+ } else {
27
+ tagOption.push('-a');
28
+ }
29
+ if (args.tagForce) {
30
+ tagOption.push('-f');
31
+ }
32
+ checkpoint(args, 'tagging release %s%s', [args.tagPrefix, newVersion]);
33
+ await runExecFile(args, 'git', [
34
+ 'tag',
35
+ ...tagOption,
36
+ args.tagPrefix + newVersion,
37
+ '-m',
38
+ `${formatCommitMessage(args)}`,
39
+ ]);
40
+ const currentBranch = await runExecFile('', 'git', [
41
+ 'rev-parse',
42
+ '--abbrev-ref',
43
+ 'HEAD',
44
+ ]);
45
+ let message = 'git push --follow-tags origin ' + currentBranch.trim();
46
+ if (pkgPrivate !== true && bump.getUpdatedConfigs()['package.json']) {
47
+ const npmPublishHint = args.npmPublishHint || (await detectPublishHint());
48
+ message += ` && ${npmPublishHint}`;
49
+ if (args.prerelease !== undefined) {
50
+ if (args.prerelease === '') {
51
+ message += ' --tag prerelease';
52
+ } else {
53
+ message += ' --tag ' + args.prerelease;
54
+ }
55
+ }
56
+ }
57
+
58
+ checkpoint(args, 'Run `%s` to publish', [message], chalk.blue(figures.info));
59
+ }
@@ -0,0 +1,15 @@
1
+ import chalk from 'chalk';
2
+
3
+ export default function (args, msg, opts) {
4
+ if (!args.silent) {
5
+ opts = Object.assign(
6
+ {
7
+ level: 'error',
8
+ color: 'red',
9
+ },
10
+ opts,
11
+ );
12
+
13
+ console[opts.level](chalk[opts.color](msg));
14
+ }
15
+ }
@@ -0,0 +1,19 @@
1
+ import { promisify } from 'util';
2
+ import printError from './print-error.js';
3
+ import { exec as execCb } from 'child_process';
4
+
5
+ const exec = promisify(execCb);
6
+
7
+ export default async function (args, cmd) {
8
+ if (args.dryRun) return;
9
+ try {
10
+ const { stderr, stdout } = await exec(cmd);
11
+ // If exec returns content in stderr, but no error, print it as a warning
12
+ if (stderr) printError(args, stderr, { level: 'warn', color: 'yellow' });
13
+ return stdout;
14
+ } catch (error) {
15
+ // If exec returns an error, print it and exit with return code 1
16
+ printError(args, error.stderr || error.message);
17
+ throw error;
18
+ }
19
+ }
@@ -0,0 +1,19 @@
1
+ import { promisify } from 'util';
2
+ import printError from './print-error.js';
3
+ import { execFile as execFileCb } from 'child_process';
4
+
5
+ const execFile = promisify(execFileCb);
6
+
7
+ export default async function (args, cmd, cmdArgs) {
8
+ if (args.dryRun) return;
9
+ try {
10
+ const { stderr, stdout } = await execFile(cmd, cmdArgs);
11
+ // If execFile returns content in stderr, but no error, print it as a warning
12
+ if (stderr) printError(args, stderr, { level: 'warn', color: 'yellow' });
13
+ return stdout;
14
+ } catch (error) {
15
+ // If execFile returns an error, print it and exit with return code 1
16
+ printError(args, error.stderr || error.message);
17
+ throw error;
18
+ }
19
+ }
@@ -0,0 +1,18 @@
1
+ import chalk from 'chalk';
2
+ import checkpoint from './checkpoint.js';
3
+ import figures from 'figures';
4
+ import runExec from './run-exec.js';
5
+
6
+ export default function (args, hookName) {
7
+ const scripts = args.scripts;
8
+ if (!scripts || !scripts[hookName]) return Promise.resolve();
9
+ const command = scripts[hookName];
10
+ checkpoint(args, 'Running lifecycle script "%s"', [hookName]);
11
+ checkpoint(
12
+ args,
13
+ '- execute command: "%s"',
14
+ [command],
15
+ chalk.blue(figures.info),
16
+ );
17
+ return runExec(args, command);
18
+ }
@@ -0,0 +1,34 @@
1
+ /*
2
+ Copyright npm, Inc
3
+
4
+ Permission to use, copy, modify, and/or distribute this software for any
5
+ purpose with or without fee is hereby granted, provided that the above
6
+ copyright notice and this permission notice appear in all copies.
7
+
8
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11
+ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15
+
16
+ https://github.com/npm/stringify-package/blob/main/LICENSE
17
+ */
18
+
19
+ 'use strict';
20
+
21
+ const DEFAULT_INDENT = 2;
22
+ const CRLF = '\r\n';
23
+ const LF = '\n';
24
+
25
+ export default function stringifyPackage(data, indent, newline) {
26
+ indent = indent || (indent === 0 ? 0 : DEFAULT_INDENT);
27
+ const json = JSON.stringify(data, null, indent);
28
+
29
+ if (newline === CRLF) {
30
+ return json.replace(/\n/g, CRLF) + CRLF;
31
+ }
32
+
33
+ return json + LF;
34
+ }