@form8ion/javascript 15.6.0 → 15.6.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.
- package/package.json +4 -3
- package/src/code-style/index.js +3 -0
- package/src/code-style/lifter.js +6 -0
- package/src/code-style/lifter.test.js +25 -0
- package/src/code-style/remark/index.js +3 -0
- package/src/code-style/remark/lifter.js +10 -0
- package/src/code-style/remark/lifter.test.js +28 -0
- package/src/code-style/remark/scaffolder.js +38 -0
- package/src/code-style/remark/scaffolder.test.js +102 -0
- package/src/code-style/remark/tester.js +11 -0
- package/src/code-style/remark/tester.test.js +46 -0
- package/src/code-style/scaffolder.js +26 -0
- package/src/code-style/scaffolder.test.js +87 -0
- package/src/code-style/tester.js +5 -0
- package/src/code-style/tester.test.js +23 -0
- package/src/corepack/index.js +1 -0
- package/src/corepack/lifter.js +5 -0
- package/src/corepack/lifter.test.js +22 -0
- package/src/coverage/index.js +3 -0
- package/src/coverage/lifter.js +31 -0
- package/src/coverage/lifter.test.js +58 -0
- package/src/coverage/nyc/index.js +2 -0
- package/src/coverage/nyc/remover.js +16 -0
- package/src/coverage/nyc/remover.test.js +24 -0
- package/src/coverage/nyc/tester.js +5 -0
- package/src/coverage/nyc/tester.test.js +29 -0
- package/src/coverage/scaffolder.js +7 -0
- package/src/coverage/scaffolder.test.js +32 -0
- package/src/coverage/tester.js +10 -0
- package/src/coverage/tester.test.js +50 -0
- package/src/dependencies/index.js +3 -0
- package/src/dependencies/installer.js +25 -0
- package/src/dependencies/installer.test.js +77 -0
- package/src/dependencies/package-managers.js +32 -0
- package/src/dependencies/package-managers.test.js +46 -0
- package/src/dependencies/processor.js +30 -0
- package/src/dependencies/processor.test.js +75 -0
- package/src/dependencies/remover.js +10 -0
- package/src/dependencies/remover.test.js +28 -0
- package/src/dialects/babel/config/ignore-adder.js +10 -0
- package/src/dialects/babel/config/ignore-adder.test.js +33 -0
- package/src/dialects/babel/config/index.js +3 -0
- package/src/dialects/babel/config/loader.js +5 -0
- package/src/dialects/babel/config/loader.test.js +21 -0
- package/src/dialects/babel/config/writer.js +6 -0
- package/src/dialects/babel/config/writer.test.js +20 -0
- package/src/dialects/babel/index.js +3 -0
- package/src/dialects/babel/lifter.js +7 -0
- package/src/dialects/babel/lifter.test.js +17 -0
- package/src/dialects/babel/predicate.js +5 -0
- package/src/dialects/babel/predicate.test.js +25 -0
- package/src/dialects/babel/scaffolder.js +14 -0
- package/src/dialects/babel/scaffolder.test.js +28 -0
- package/src/dialects/index.js +2 -0
- package/src/dialects/prompt-choices.js +10 -0
- package/src/dialects/prompt-choices.test.js +28 -0
- package/src/dialects/scaffolder.js +15 -0
- package/src/dialects/scaffolder.test.js +49 -0
- package/src/dialects/typescript/index.js +1 -0
- package/src/dialects/typescript/scaffolder.js +31 -0
- package/src/dialects/typescript/scaffolder.test.js +95 -0
- package/src/documentation/generation-command.js +11 -0
- package/src/documentation/generation-command.test.js +25 -0
- package/src/documentation/index.js +1 -0
- package/src/documentation/scaffolder.js +20 -0
- package/src/documentation/scaffolder.test.js +49 -0
- package/src/engines/index.js +2 -0
- package/src/engines/lifter.js +7 -0
- package/src/engines/lifter.test.js +18 -0
- package/src/engines/tester.js +7 -0
- package/src/engines/tester.test.js +37 -0
- package/src/index.js +9 -0
- package/src/lifter.js +55 -0
- package/src/lifter.test.js +96 -0
- package/src/linting/index.js +1 -0
- package/src/linting/scaffolder.js +5 -0
- package/src/linting/scaffolder.test.js +31 -0
- package/src/lockfile-lint/allowed-hosts-builder.js +6 -0
- package/src/lockfile-lint/allowed-hosts-builder.test.js +35 -0
- package/src/lockfile-lint/config.js +12 -0
- package/src/lockfile-lint/config.test.js +37 -0
- package/src/lockfile-lint/index.js +3 -0
- package/src/lockfile-lint/scaffolder.js +38 -0
- package/src/lockfile-lint/scaffolder.test.js +85 -0
- package/src/lockfile-lint/tester.js +5 -0
- package/src/lockfile-lint/tester.test.js +25 -0
- package/src/node-version/index.js +2 -0
- package/src/node-version/scaffolder.js +19 -0
- package/src/node-version/scaffolder.test.js +33 -0
- package/src/node-version/tasks.js +25 -0
- package/src/node-version/tasks.test.js +43 -0
- package/src/node-version/tester.js +5 -0
- package/src/node-version/tester.test.js +29 -0
- package/src/npm-config/index.js +5 -0
- package/src/npm-config/lifter.js +14 -0
- package/src/npm-config/lifter.test.js +23 -0
- package/src/npm-config/reader.js +11 -0
- package/src/npm-config/reader.test.js +33 -0
- package/src/npm-config/scaffolder.js +16 -0
- package/src/npm-config/scaffolder.test.js +54 -0
- package/src/npm-config/tester.js +5 -0
- package/src/npm-config/tester.test.js +29 -0
- package/src/npm-config/writer.js +6 -0
- package/src/npm-config/writer.test.js +24 -0
- package/src/options/schemas.js +14 -0
- package/src/options/schemas.test.js +147 -0
- package/src/options/validator.js +45 -0
- package/src/options/validator.test.js +79 -0
- package/src/package/details.js +18 -0
- package/src/package/details.test.js +51 -0
- package/src/package/index.js +2 -0
- package/src/package/lifter.js +47 -0
- package/src/package/lifter.test.js +100 -0
- package/src/package/package-name.js +13 -0
- package/src/package/package-name.test.js +52 -0
- package/src/package/property-sorter.js +38 -0
- package/src/package/property-sorter.test.js +56 -0
- package/src/package/scaffolder.js +32 -0
- package/src/package/scaffolder.test.js +46 -0
- package/src/package/scripts/index.js +1 -0
- package/src/package/scripts/lifter.js +14 -0
- package/src/package/scripts/lifter.test.js +31 -0
- package/src/package/scripts/script-comparator.js +46 -0
- package/src/package/scripts/script-comparator.test.js +119 -0
- package/src/package/scripts/scripts-sorter.js +7 -0
- package/src/package/scripts/scripts-sorter.test.js +20 -0
- package/src/package/scripts/test-script-updater.js +15 -0
- package/src/package/scripts/test-script-updater.test.js +32 -0
- package/src/package/vcs-host-details.js +12 -0
- package/src/package/vcs-host-details.test.js +16 -0
- package/src/package-managers/current-manager-resolver.js +21 -0
- package/src/package-managers/current-manager-resolver.test.js +51 -0
- package/src/package-managers/index.js +5 -0
- package/src/package-managers/lifter.js +7 -0
- package/src/package-managers/lifter.test.js +18 -0
- package/src/package-managers/lockfile-path-resolver.js +10 -0
- package/src/package-managers/lockfile-path-resolver.test.js +15 -0
- package/src/package-managers/npm/index.js +2 -0
- package/src/package-managers/npm/scaffolder.js +19 -0
- package/src/package-managers/npm/scaffolder.test.js +33 -0
- package/src/package-managers/npm/tester.js +11 -0
- package/src/package-managers/npm/tester.test.js +33 -0
- package/src/package-managers/scaffolder.js +11 -0
- package/src/package-managers/scaffolder.test.js +27 -0
- package/src/package-managers/tester.js +11 -0
- package/src/package-managers/tester.test.js +33 -0
- package/src/package-managers/yarn/index.js +2 -0
- package/src/package-managers/yarn/scaffolder.js +19 -0
- package/src/package-managers/yarn/scaffolder.test.js +33 -0
- package/src/package-managers/yarn/tester.js +11 -0
- package/src/package-managers/yarn/tester.test.js +33 -0
- package/src/plugins-schemas.js +4 -0
- package/src/plugins-schemas.test.js +28 -0
- package/src/project-type/application/index.js +2 -0
- package/src/project-type/application/predicate.js +3 -0
- package/src/project-type/application/predicate.test.js +14 -0
- package/src/project-type/application/scaffolder.js +24 -0
- package/src/project-type/application/scaffolder.test.js +35 -0
- package/src/project-type/cli/index.js +3 -0
- package/src/project-type/cli/lifter.js +5 -0
- package/src/project-type/cli/lifter.test.js +20 -0
- package/src/project-type/cli/scaffolder.js +52 -0
- package/src/project-type/cli/scaffolder.test.js +103 -0
- package/src/project-type/cli/tester.js +3 -0
- package/src/project-type/cli/tester.test.js +14 -0
- package/src/project-type/index.js +3 -0
- package/src/project-type/lifter.js +23 -0
- package/src/project-type/lifter.test.js +69 -0
- package/src/project-type/monorepo/index.js +1 -0
- package/src/project-type/monorepo/scaffolder.js +16 -0
- package/src/project-type/monorepo/scaffolder.test.js +27 -0
- package/src/project-type/package/build-details.js +56 -0
- package/src/project-type/package/build-details.test.js +111 -0
- package/src/project-type/package/documentation.js +34 -0
- package/src/project-type/package/documentation.test.js +106 -0
- package/src/project-type/package/index.js +3 -0
- package/src/project-type/package/lifter.js +5 -0
- package/src/project-type/package/lifter.test.js +20 -0
- package/src/project-type/package/scaffolder.js +84 -0
- package/src/project-type/package/scaffolder.test.js +267 -0
- package/src/project-type/package/tester.js +5 -0
- package/src/project-type/package/tester.test.js +28 -0
- package/src/project-type/publishable/access-level.js +3 -0
- package/src/project-type/publishable/access-level.test.js +13 -0
- package/src/project-type/publishable/badges.js +20 -0
- package/src/project-type/publishable/badges.test.js +29 -0
- package/src/project-type/publishable/bundler/index.js +1 -0
- package/src/project-type/publishable/bundler/prompt.js +16 -0
- package/src/project-type/publishable/bundler/prompt.test.js +35 -0
- package/src/project-type/publishable/bundler/scaffolder.js +8 -0
- package/src/project-type/publishable/bundler/scaffolder.test.js +33 -0
- package/src/project-type/publishable/index.js +2 -0
- package/src/project-type/publishable/lifter.js +24 -0
- package/src/project-type/publishable/lifter.test.js +49 -0
- package/src/project-type/publishable/provenance/index.js +1 -0
- package/src/project-type/publishable/provenance/lifter.js +15 -0
- package/src/project-type/publishable/provenance/lifter.test.js +56 -0
- package/src/project-type/publishable/provenance/slsa.js +17 -0
- package/src/project-type/publishable/provenance/slsa.test.js +21 -0
- package/src/project-type/publishable/registry-resolver.js +15 -0
- package/src/project-type/publishable/registry-resolver.test.js +60 -0
- package/src/project-type/publishable/scaffolder.js +7 -0
- package/src/project-type/publishable/scaffolder.test.js +23 -0
- package/src/project-type/scaffolder.js +56 -0
- package/src/project-type/scaffolder.test.js +115 -0
- package/src/project-type/tester.js +9 -0
- package/src/project-type/tester.test.js +51 -0
- package/src/project-type-plugin/index.js +1 -0
- package/src/project-type-plugin/prompt.js +16 -0
- package/src/project-type-plugin/prompt.test.js +39 -0
- package/src/project-type-plugin/scaffolder.js +28 -0
- package/src/project-type-plugin/scaffolder.test.js +70 -0
- package/src/prompts/conditionals.js +39 -0
- package/src/prompts/conditionals.test.js +95 -0
- package/src/prompts/question-names.js +17 -0
- package/src/prompts/questions.js +158 -0
- package/src/prompts/questions.test.js +247 -0
- package/src/prompts/validators.js +9 -0
- package/src/prompts/validators.test.js +19 -0
- package/src/registries/index.js +2 -0
- package/src/registries/lifter.js +43 -0
- package/src/registries/lifter.test.js +63 -0
- package/src/registries/npm-config/list-builder.js +9 -0
- package/src/registries/npm-config/list-builder.test.js +43 -0
- package/src/registries/tester.js +3 -0
- package/src/registries/tester.test.js +9 -0
- package/src/runkit/badge/index.js +1 -0
- package/src/runkit/badge/scaffolder.js +13 -0
- package/src/runkit/badge/scaffolder.test.js +22 -0
- package/src/runkit/index.js +4 -0
- package/src/runkit/lifter.js +5 -0
- package/src/runkit/lifter.test.js +17 -0
- package/src/runkit/remover.js +3 -0
- package/src/runkit/remover.test.js +9 -0
- package/src/runkit/scaffolder.js +11 -0
- package/src/runkit/scaffolder.test.js +35 -0
- package/src/runkit/tester.js +3 -0
- package/src/runkit/tester.test.js +16 -0
- package/src/scaffolder.js +155 -0
- package/src/scaffolder.test.js +239 -0
- package/src/tester.js +17 -0
- package/src/tester.test.js +37 -0
- package/src/testing/index.js +1 -0
- package/src/testing/scaffolder.js +31 -0
- package/src/testing/scaffolder.test.js +63 -0
- package/src/testing/unit/index.js +1 -0
- package/src/testing/unit/prompt.js +15 -0
- package/src/testing/unit/prompt.test.js +33 -0
- package/src/testing/unit/scaffolder.js +30 -0
- package/src/testing/unit/scaffolder.test.js +54 -0
- package/src/vcs/ignore-lists-builder.js +6 -0
- package/src/vcs/ignore-lists-builder.test.js +25 -0
- package/src/vcs/schema.js +7 -0
- package/src/vcs/schema.test.js +40 -0
- package/src/verification/index.js +1 -0
- package/src/verification/scaffolder.js +35 -0
- package/src/verification/scaffolder.test.js +56 -0
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import {describe, expect, it} from 'vitest';
|
|
2
|
+
import any from '@travi/any';
|
|
3
|
+
|
|
4
|
+
import test from './tester.js';
|
|
5
|
+
|
|
6
|
+
describe('package project-type tester', () => {
|
|
7
|
+
it('should return `true` if the project defines `exports`', async () => {
|
|
8
|
+
expect(await test({packageDetails: {...any.simpleObject(), exports: any.word()}})).toBe(true);
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
it('should return `true` if the project defines `publishConfig`', async () => {
|
|
12
|
+
expect(await test({packageDetails: {...any.simpleObject(), publishConfig: any.simpleObject()}})).toBe(true);
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
it('should return `false` if the project defines `bin` in addition to `publishConfig`', async () => {
|
|
16
|
+
expect(await test({
|
|
17
|
+
packageDetails: {...any.simpleObject(), publishConfig: any.simpleObject(), bin: any.simpleObject()}
|
|
18
|
+
})).toBe(false);
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it('should return `false` when there are no indicators that the project is a package type', async () => {
|
|
22
|
+
expect(await test({packageDetails: any.simpleObject()})).toBe(false);
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it('should return `false` when `private` is set to `true`', async () => {
|
|
26
|
+
expect(await test({packageDetails: {...any.simpleObject(), private: true, exports: any.word()}})).toBe(false);
|
|
27
|
+
});
|
|
28
|
+
});
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import {describe, expect, it} from 'vitest';
|
|
2
|
+
|
|
3
|
+
import determinePackageAccessLevelFromProjectVisibility from './access-level.js';
|
|
4
|
+
|
|
5
|
+
describe('package access level', () => {
|
|
6
|
+
it('should return `public` when project visibility is `Public`', () => {
|
|
7
|
+
expect(determinePackageAccessLevelFromProjectVisibility({projectVisibility: 'Public'})).toEqual('public');
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
it('should return `restricted` when the project visibility is `Private`', () => {
|
|
11
|
+
expect(determinePackageAccessLevelFromProjectVisibility({projectVisibility: 'Private'})).toEqual('restricted');
|
|
12
|
+
});
|
|
13
|
+
});
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
function buildNpmBadgeImageUrl(packageName, customRegistry) {
|
|
2
|
+
const params = new URLSearchParams({logo: 'npm', ...customRegistry && {registry_uri: customRegistry}});
|
|
3
|
+
|
|
4
|
+
return `https://img.shields.io/npm/v/${packageName}?${params}`;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export default function scaffoldPublishableBadges({packageName, accessLevel, customRegistry}) {
|
|
8
|
+
return {
|
|
9
|
+
consumer: {
|
|
10
|
+
...'public' === accessLevel && {
|
|
11
|
+
npm: {
|
|
12
|
+
img: buildNpmBadgeImageUrl(packageName, customRegistry),
|
|
13
|
+
text: 'npm',
|
|
14
|
+
link: `https://www.npmjs.com/package/${packageName}`
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
status: {}
|
|
19
|
+
};
|
|
20
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import {describe, it, expect} from 'vitest';
|
|
2
|
+
import any from '@travi/any';
|
|
3
|
+
|
|
4
|
+
import defineBadges from './badges.js';
|
|
5
|
+
|
|
6
|
+
describe('badges for publishable project types', async () => {
|
|
7
|
+
const packageName = any.word();
|
|
8
|
+
const npmBadgeDetails = {
|
|
9
|
+
img: `https://img.shields.io/npm/v/${packageName}?logo=npm`,
|
|
10
|
+
text: 'npm',
|
|
11
|
+
link: `https://www.npmjs.com/package/${packageName}`
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
it('should define the badges when the access level is not public', () => {
|
|
15
|
+
expect(defineBadges(packageName)).toEqual({consumer: {}, status: {}});
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it('should return the npm badge for packages with a public access level', () => {
|
|
19
|
+
expect(defineBadges({packageName, accessLevel: 'public'}).consumer).toEqual({npm: npmBadgeDetails});
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it('should include the registry_uri in the npm badge when a custom registry is provided', () => {
|
|
23
|
+
const customRegistry = any.url();
|
|
24
|
+
|
|
25
|
+
const {searchParams} = new URL(defineBadges({packageName, accessLevel: 'public', customRegistry}).consumer.npm.img);
|
|
26
|
+
|
|
27
|
+
expect(searchParams.get('registry_uri')).toEqual(customRegistry);
|
|
28
|
+
});
|
|
29
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {default as scaffold} from './scaffolder.js';
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import {prompt} from '@form8ion/overridable-prompts';
|
|
2
|
+
|
|
3
|
+
import {questionNames} from '../../../prompts/question-names.js';
|
|
4
|
+
|
|
5
|
+
export default async function gatherBundlerInput({bundlers, decisions}) {
|
|
6
|
+
if (!Object.keys(bundlers).length) return 'Other';
|
|
7
|
+
|
|
8
|
+
const answers = await prompt([{
|
|
9
|
+
name: questionNames.PACKAGE_BUNDLER,
|
|
10
|
+
type: 'list',
|
|
11
|
+
message: 'Which bundler should be used?',
|
|
12
|
+
choices: [...Object.keys(bundlers), 'Other']
|
|
13
|
+
}], decisions);
|
|
14
|
+
|
|
15
|
+
return answers[questionNames.PACKAGE_BUNDLER];
|
|
16
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import * as prompts from '@form8ion/overridable-prompts';
|
|
2
|
+
|
|
3
|
+
import {afterEach, describe, expect, it, vi} from 'vitest';
|
|
4
|
+
import any from '@travi/any';
|
|
5
|
+
import {when} from 'vitest-when';
|
|
6
|
+
|
|
7
|
+
import {questionNames} from '../../../prompts/question-names.js';
|
|
8
|
+
import prompt from './prompt.js';
|
|
9
|
+
|
|
10
|
+
vi.mock('@form8ion/overridable-prompts');
|
|
11
|
+
|
|
12
|
+
describe('bundler prompt', () => {
|
|
13
|
+
afterEach(() => {
|
|
14
|
+
vi.clearAllMocks();
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it('should present the choice of package bundlers', async () => {
|
|
18
|
+
const chosenType = any.word();
|
|
19
|
+
const decisions = any.simpleObject();
|
|
20
|
+
const answers = {...any.simpleObject(), [questionNames.PACKAGE_BUNDLER]: chosenType};
|
|
21
|
+
const bundlers = any.simpleObject();
|
|
22
|
+
when(prompts.prompt).calledWith([{
|
|
23
|
+
name: questionNames.PACKAGE_BUNDLER,
|
|
24
|
+
type: 'list',
|
|
25
|
+
message: 'Which bundler should be used?',
|
|
26
|
+
choices: [...Object.keys(bundlers), 'Other']
|
|
27
|
+
}], decisions).thenResolve(answers);
|
|
28
|
+
|
|
29
|
+
expect(await prompt({bundlers, decisions})).toEqual(chosenType);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it('should skip the prompt and return `Other` when no options are provided', async () => {
|
|
33
|
+
expect(await prompt({bundlers: {}})).toEqual('Other');
|
|
34
|
+
});
|
|
35
|
+
});
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import {scaffoldChoice as scaffoldChosenBundler} from '@form8ion/javascript-core';
|
|
2
|
+
import chooseBundler from './prompt.js';
|
|
3
|
+
|
|
4
|
+
export default async function scaffoldBundler({projectRoot, projectType, bundlers, dialect, decisions}) {
|
|
5
|
+
const chosenBundler = await chooseBundler({bundlers, decisions});
|
|
6
|
+
|
|
7
|
+
return scaffoldChosenBundler(bundlers, chosenBundler, {projectRoot, projectType, dialect});
|
|
8
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import {scaffoldChoice as scaffoldChosenBundler} from '@form8ion/javascript-core';
|
|
2
|
+
|
|
3
|
+
import {afterEach, describe, expect, it, vi} from 'vitest';
|
|
4
|
+
import {when} from 'vitest-when';
|
|
5
|
+
import any from '@travi/any';
|
|
6
|
+
|
|
7
|
+
import chooseBundler from './prompt.js';
|
|
8
|
+
import scaffoldBundler from './scaffolder.js';
|
|
9
|
+
|
|
10
|
+
vi.mock('@form8ion/javascript-core');
|
|
11
|
+
vi.mock('./prompt');
|
|
12
|
+
|
|
13
|
+
describe('package bundler scaffolder', () => {
|
|
14
|
+
afterEach(() => {
|
|
15
|
+
vi.clearAllMocks();
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it('should enable choosing from the available bundlers and scaffold the chosen one', async () => {
|
|
19
|
+
const projectRoot = any.string();
|
|
20
|
+
const dialect = any.word();
|
|
21
|
+
const projectType = any.word();
|
|
22
|
+
const bundlers = any.simpleObject();
|
|
23
|
+
const decisions = any.simpleObject();
|
|
24
|
+
const bundlerResults = any.simpleObject();
|
|
25
|
+
const chosenBundler = any.word();
|
|
26
|
+
when(chooseBundler).calledWith({bundlers, decisions}).thenResolve(chosenBundler);
|
|
27
|
+
when(scaffoldChosenBundler)
|
|
28
|
+
.calledWith(bundlers, chosenBundler, {projectRoot, dialect, projectType})
|
|
29
|
+
.thenResolve(bundlerResults);
|
|
30
|
+
|
|
31
|
+
expect(await scaffoldBundler({bundlers, projectRoot, projectType, dialect, decisions})).toEqual(bundlerResults);
|
|
32
|
+
});
|
|
33
|
+
});
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import deepmerge from 'deepmerge';
|
|
2
|
+
import {mergeIntoExistingPackageJson} from '@form8ion/javascript-core';
|
|
3
|
+
|
|
4
|
+
import resolveRegistry from './registry-resolver.js';
|
|
5
|
+
import defineBadges from './badges.js';
|
|
6
|
+
import {lift as liftProvenance} from './provenance/index.js';
|
|
7
|
+
|
|
8
|
+
export default async function liftPublishable({projectRoot, packageDetails, configs}) {
|
|
9
|
+
const {name: packageName, publishConfig: {access: packageAccessLevel}} = packageDetails;
|
|
10
|
+
const homepage = `https://npm.im/${packageName}`;
|
|
11
|
+
const customRegistry = resolveRegistry(packageName, configs.registries);
|
|
12
|
+
|
|
13
|
+
await mergeIntoExistingPackageJson({projectRoot, config: {homepage}});
|
|
14
|
+
|
|
15
|
+
return deepmerge(
|
|
16
|
+
await liftProvenance({packageDetails, projectRoot, customRegistry}),
|
|
17
|
+
{
|
|
18
|
+
homepage,
|
|
19
|
+
dependencies: {javascript: {development: ['publint']}},
|
|
20
|
+
scripts: {'lint:publish': 'publint --strict'},
|
|
21
|
+
badges: defineBadges({packageName, accessLevel: packageAccessLevel, customRegistry})
|
|
22
|
+
}
|
|
23
|
+
);
|
|
24
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import deepmerge from 'deepmerge';
|
|
2
|
+
import {mergeIntoExistingPackageJson} from '@form8ion/javascript-core';
|
|
3
|
+
|
|
4
|
+
import {describe, expect, it, vi} from 'vitest';
|
|
5
|
+
import any from '@travi/any';
|
|
6
|
+
import {when} from 'vitest-when';
|
|
7
|
+
|
|
8
|
+
import resolveRegistry from './registry-resolver.js';
|
|
9
|
+
import defineBadges from './badges.js';
|
|
10
|
+
import {lift as liftProvenance} from './provenance/index.js';
|
|
11
|
+
import lift from './lifter.js';
|
|
12
|
+
|
|
13
|
+
vi.mock('deepmerge');
|
|
14
|
+
vi.mock('@form8ion/javascript-core');
|
|
15
|
+
vi.mock('./provenance');
|
|
16
|
+
vi.mock('./badges');
|
|
17
|
+
vi.mock('./registry-resolver.js');
|
|
18
|
+
|
|
19
|
+
describe('publishable project-type lifter', () => {
|
|
20
|
+
it('should lift the details of the package project', async () => {
|
|
21
|
+
const projectRoot = any.string();
|
|
22
|
+
const packageName = any.word();
|
|
23
|
+
const packageAccessLevel = any.word();
|
|
24
|
+
const registries = any.simpleObject();
|
|
25
|
+
const customRegistry = any.url();
|
|
26
|
+
const packageDetails = {...any.simpleObject(), name: packageName, publishConfig: {access: packageAccessLevel}};
|
|
27
|
+
const provenanceResults = any.simpleObject();
|
|
28
|
+
const mergedResults = any.simpleObject();
|
|
29
|
+
const badgesResults = any.simpleObject();
|
|
30
|
+
const homepage = `https://npm.im/${packageName}`;
|
|
31
|
+
when(resolveRegistry).calledWith(packageName, registries).thenReturn(customRegistry);
|
|
32
|
+
when(liftProvenance).calledWith({packageDetails, projectRoot, customRegistry}).thenResolve(provenanceResults);
|
|
33
|
+
when(defineBadges)
|
|
34
|
+
.calledWith({packageName, accessLevel: packageAccessLevel, customRegistry})
|
|
35
|
+
.thenReturn(badgesResults);
|
|
36
|
+
when(deepmerge).calledWith(
|
|
37
|
+
provenanceResults,
|
|
38
|
+
{
|
|
39
|
+
scripts: {'lint:publish': 'publint --strict'},
|
|
40
|
+
dependencies: {javascript: {development: ['publint']}},
|
|
41
|
+
badges: badgesResults,
|
|
42
|
+
homepage
|
|
43
|
+
}
|
|
44
|
+
).thenReturn(mergedResults);
|
|
45
|
+
|
|
46
|
+
expect(await lift({projectRoot, packageDetails, configs: {registries}})).toEqual(mergedResults);
|
|
47
|
+
expect(mergeIntoExistingPackageJson).toHaveBeenCalledWith({projectRoot, config: {homepage}});
|
|
48
|
+
});
|
|
49
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {default as lift} from './lifter.js';
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import {mergeIntoExistingPackageJson} from '@form8ion/javascript-core';
|
|
2
|
+
|
|
3
|
+
import enhanceSlsa from './slsa.js';
|
|
4
|
+
|
|
5
|
+
export default async function liftProvenance({projectRoot, packageDetails, customRegistry}) {
|
|
6
|
+
const {publishConfig: {access} = {}} = packageDetails;
|
|
7
|
+
|
|
8
|
+
if ('public' === access && !customRegistry) {
|
|
9
|
+
await mergeIntoExistingPackageJson({projectRoot, config: {publishConfig: {provenance: true}}});
|
|
10
|
+
|
|
11
|
+
return enhanceSlsa({provenance: true});
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
return {};
|
|
15
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import {mergeIntoExistingPackageJson} from '@form8ion/javascript-core';
|
|
2
|
+
|
|
3
|
+
import {describe, vi, it, expect, afterEach} from 'vitest';
|
|
4
|
+
import any from '@travi/any';
|
|
5
|
+
import {when} from 'vitest-when';
|
|
6
|
+
|
|
7
|
+
import enhanceSlsa from './slsa.js';
|
|
8
|
+
import lift from './lifter.js';
|
|
9
|
+
|
|
10
|
+
vi.mock('@form8ion/javascript-core');
|
|
11
|
+
vi.mock('./slsa.js');
|
|
12
|
+
|
|
13
|
+
describe('provenance lifter', () => {
|
|
14
|
+
const projectRoot = any.string();
|
|
15
|
+
|
|
16
|
+
afterEach(() => {
|
|
17
|
+
vi.clearAllMocks();
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it('should configure provenance for a public package published to the official registry', async () => {
|
|
21
|
+
const packageDetails = {...any.simpleObject(), publishConfig: {access: 'public'}};
|
|
22
|
+
const slsaResults = any.simpleObject();
|
|
23
|
+
when(enhanceSlsa).calledWith({provenance: true}).thenReturn(slsaResults);
|
|
24
|
+
|
|
25
|
+
expect(await lift({packageDetails, projectRoot})).toEqual(slsaResults);
|
|
26
|
+
expect(mergeIntoExistingPackageJson).toHaveBeenCalledWith({
|
|
27
|
+
projectRoot,
|
|
28
|
+
config: {publishConfig: {provenance: true}}
|
|
29
|
+
});
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it('should not configure provenance for a public package published to an alternative registry', async () => {
|
|
33
|
+
const packageDetails = {...any.simpleObject(), publishConfig: {access: 'public'}};
|
|
34
|
+
const customRegistry = any.url();
|
|
35
|
+
|
|
36
|
+
expect(await lift({packageDetails, projectRoot, customRegistry})).toEqual({});
|
|
37
|
+
expect(enhanceSlsa).not.toHaveBeenCalled();
|
|
38
|
+
expect(mergeIntoExistingPackageJson).not.toHaveBeenCalled();
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it('should not configure provenance for a restricted package', async () => {
|
|
42
|
+
const packageDetails = {...any.simpleObject(), publishConfig: {access: 'restricted'}};
|
|
43
|
+
|
|
44
|
+
expect(await lift({packageDetails, projectRoot})).toEqual({});
|
|
45
|
+
expect(enhanceSlsa).not.toHaveBeenCalled();
|
|
46
|
+
expect(mergeIntoExistingPackageJson).not.toHaveBeenCalled();
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it('should not configure provenance for a package without `publishConfig`', async () => {
|
|
50
|
+
const packageDetails = any.simpleObject();
|
|
51
|
+
|
|
52
|
+
expect(await lift({packageDetails, projectRoot})).toEqual({});
|
|
53
|
+
expect(enhanceSlsa).not.toHaveBeenCalled();
|
|
54
|
+
expect(mergeIntoExistingPackageJson).not.toHaveBeenCalled();
|
|
55
|
+
});
|
|
56
|
+
});
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export default function scaffoldSlsa({provenance}) {
|
|
2
|
+
if (provenance) {
|
|
3
|
+
return {
|
|
4
|
+
badges: {
|
|
5
|
+
status: {
|
|
6
|
+
slsa: {
|
|
7
|
+
img: 'https://slsa.dev/images/gh-badge-level2.svg',
|
|
8
|
+
url: 'https://slsa.dev',
|
|
9
|
+
text: 'SLSA Level 2'
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
return {};
|
|
17
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import {describe, expect, it} from 'vitest';
|
|
2
|
+
|
|
3
|
+
import determineSlsaLevel from './slsa.js';
|
|
4
|
+
|
|
5
|
+
describe('SLSA badge', () => {
|
|
6
|
+
it('should return the SLSA Level 2 badge for public publishable projects that publish with provenance', () => {
|
|
7
|
+
const {badges} = determineSlsaLevel({provenance: true});
|
|
8
|
+
|
|
9
|
+
expect(badges.status.slsa).toEqual({
|
|
10
|
+
img: 'https://slsa.dev/images/gh-badge-level2.svg',
|
|
11
|
+
url: 'https://slsa.dev',
|
|
12
|
+
text: 'SLSA Level 2'
|
|
13
|
+
});
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
it('should not return a badge if the package is not published with provenance', () => {
|
|
17
|
+
const {badges} = determineSlsaLevel({});
|
|
18
|
+
|
|
19
|
+
expect(badges).toBe(undefined);
|
|
20
|
+
});
|
|
21
|
+
});
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
function isScope(firstPart) {
|
|
2
|
+
return firstPart.startsWith('@');
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
export default function resolveRegistry(packageName, registries = {}) {
|
|
6
|
+
if (registries.publish) return registries.publish;
|
|
7
|
+
|
|
8
|
+
const [firstPart] = packageName.split('/');
|
|
9
|
+
if (isScope(firstPart)) {
|
|
10
|
+
const scopedRegistry = registries[firstPart.slice(1)];
|
|
11
|
+
if (scopedRegistry) return scopedRegistry;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
return registries.registry;
|
|
15
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import {describe, it, expect} from 'vitest';
|
|
2
|
+
import any from '@travi/any';
|
|
3
|
+
|
|
4
|
+
import resolveRegistry from './registry-resolver.js';
|
|
5
|
+
|
|
6
|
+
describe('registry resolver', () => {
|
|
7
|
+
const scope = any.word();
|
|
8
|
+
const scopedRegistry = any.url();
|
|
9
|
+
const registry = any.url();
|
|
10
|
+
|
|
11
|
+
it('should return undefined when no registries are defined', () => {
|
|
12
|
+
expect(resolveRegistry(any.word())).toBeUndefined();
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
it('should return the main registry override when defined', () => {
|
|
16
|
+
expect(resolveRegistry(any.word(), {registry})).toEqual(registry);
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it('should return the publish registry when defined', () => {
|
|
20
|
+
const publishRegistry = any.url();
|
|
21
|
+
|
|
22
|
+
expect(resolveRegistry(any.word(), {publish: publishRegistry})).toEqual(publishRegistry);
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it('should prefer the publish registry over the main registry override', () => {
|
|
26
|
+
const publishRegistry = any.url();
|
|
27
|
+
|
|
28
|
+
expect(resolveRegistry(any.word(), {registry, publish: publishRegistry})).toEqual(publishRegistry);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it('should prefer the publish registry over the scoped registry', () => {
|
|
32
|
+
const publishRegistry = any.url();
|
|
33
|
+
|
|
34
|
+
expect(resolveRegistry(`@${scope}/${any.word()}`, {[scope]: scopedRegistry, publish: publishRegistry}))
|
|
35
|
+
.toEqual(publishRegistry);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it('should return the scoped registry when the package is under a scope with a defined registry', () => {
|
|
39
|
+
expect(resolveRegistry(`@${scope}/${any.word()}`, {[scope]: scopedRegistry})).toEqual(scopedRegistry);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it('should prefer the scoped registry over the main registry override', () => {
|
|
43
|
+
expect(resolveRegistry(
|
|
44
|
+
`@${scope}/${any.word()}`,
|
|
45
|
+
{registry: any.url(), [scope]: scopedRegistry}
|
|
46
|
+
)).toEqual(scopedRegistry);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it('should return undefined when no registry matches the package scope', () => {
|
|
50
|
+
expect(resolveRegistry(`@${scope}/${any.word()}`, {[any.word()]: any.url()})).toBeUndefined();
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it('should fall back to the main registry when the package is scoped but has no matching scope registry', () => {
|
|
54
|
+
expect(resolveRegistry(`@${scope}/${any.word()}`, {registry})).toEqual(registry);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it('should return undefined when the package has no scope and no main registry is defined', () => {
|
|
58
|
+
expect(resolveRegistry(any.word(), {[any.word()]: any.url()})).toBeUndefined();
|
|
59
|
+
});
|
|
60
|
+
});
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import {afterEach, describe, expect, it, vi} from 'vitest';
|
|
2
|
+
import any from '@travi/any';
|
|
3
|
+
import {when} from 'vitest-when';
|
|
4
|
+
|
|
5
|
+
import scaffoldBadges from './badges.js';
|
|
6
|
+
import scaffoldPublishable from './scaffolder.js';
|
|
7
|
+
|
|
8
|
+
vi.mock('./badges');
|
|
9
|
+
|
|
10
|
+
describe('publishable project-type scaffolder', () => {
|
|
11
|
+
afterEach(() => {
|
|
12
|
+
vi.clearAllMocks();
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
it('should scaffold common details of a package project', async () => {
|
|
16
|
+
const packageName = any.word();
|
|
17
|
+
const packageAccessLevel = any.word();
|
|
18
|
+
const badgesResults = any.simpleObject();
|
|
19
|
+
when(scaffoldBadges).calledWith(packageName, packageAccessLevel).thenReturn(badgesResults);
|
|
20
|
+
|
|
21
|
+
expect(await scaffoldPublishable({packageName, packageAccessLevel})).toEqual({badges: badgesResults});
|
|
22
|
+
});
|
|
23
|
+
});
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import {projectTypes} from '@form8ion/javascript-core';
|
|
2
|
+
|
|
3
|
+
import {scaffold as scaffoldPackageType} from './package/index.js';
|
|
4
|
+
import {scaffold as scaffoldApplicationType} from './application/index.js';
|
|
5
|
+
import {scaffold as scaffoldMonorepoType} from './monorepo/index.js';
|
|
6
|
+
import {scaffold as scaffoldCliType} from './cli/index.js';
|
|
7
|
+
|
|
8
|
+
export default async function scaffoldProjectType({
|
|
9
|
+
projectType,
|
|
10
|
+
projectRoot,
|
|
11
|
+
projectName,
|
|
12
|
+
packageName,
|
|
13
|
+
packageManager,
|
|
14
|
+
visibility,
|
|
15
|
+
packageBundlers,
|
|
16
|
+
scope,
|
|
17
|
+
decisions,
|
|
18
|
+
dialect,
|
|
19
|
+
provideExample,
|
|
20
|
+
publishRegistry
|
|
21
|
+
}) {
|
|
22
|
+
switch (projectType) {
|
|
23
|
+
case projectTypes.PACKAGE:
|
|
24
|
+
return scaffoldPackageType({
|
|
25
|
+
projectRoot,
|
|
26
|
+
projectName,
|
|
27
|
+
packageName,
|
|
28
|
+
packageManager,
|
|
29
|
+
visibility,
|
|
30
|
+
scope,
|
|
31
|
+
packageBundlers,
|
|
32
|
+
decisions,
|
|
33
|
+
dialect,
|
|
34
|
+
provideExample,
|
|
35
|
+
publishRegistry
|
|
36
|
+
});
|
|
37
|
+
case projectTypes.APPLICATION:
|
|
38
|
+
return scaffoldApplicationType({projectRoot});
|
|
39
|
+
case projectTypes.CLI:
|
|
40
|
+
return scaffoldCliType({
|
|
41
|
+
packageName,
|
|
42
|
+
visibility,
|
|
43
|
+
projectRoot,
|
|
44
|
+
dialect,
|
|
45
|
+
publishRegistry,
|
|
46
|
+
decisions,
|
|
47
|
+
packageBundlers
|
|
48
|
+
});
|
|
49
|
+
case projectTypes.MONOREPO:
|
|
50
|
+
return scaffoldMonorepoType({projectRoot});
|
|
51
|
+
case 'Other':
|
|
52
|
+
return {};
|
|
53
|
+
default:
|
|
54
|
+
throw new Error(`The project-type of ${projectType} is invalid`);
|
|
55
|
+
}
|
|
56
|
+
}
|