@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,56 @@
|
|
|
1
|
+
import {promises as fs} from 'node:fs';
|
|
2
|
+
import deepmerge from 'deepmerge';
|
|
3
|
+
import touch from 'touch';
|
|
4
|
+
import {dialects, projectTypes} from '@form8ion/javascript-core';
|
|
5
|
+
import {scaffold as scaffoldBundler} from '../publishable/bundler/index.js';
|
|
6
|
+
|
|
7
|
+
const defaultBuildDirectory = 'lib';
|
|
8
|
+
|
|
9
|
+
async function createExample(projectRoot) {
|
|
10
|
+
return fs.writeFile(`${projectRoot}/example.js`, "import {} from './lib/index.js';\n");
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
async function buildDetailsForCommonJsProject({projectRoot, provideExample}) {
|
|
14
|
+
await Promise.all([
|
|
15
|
+
touch(`${projectRoot}/index.js`),
|
|
16
|
+
provideExample
|
|
17
|
+
? fs.writeFile(`${projectRoot}/example.js`, "const {} = require('.');\n")
|
|
18
|
+
: Promise.resolve()
|
|
19
|
+
]);
|
|
20
|
+
|
|
21
|
+
return {};
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export default async function buildDetails({
|
|
25
|
+
projectRoot,
|
|
26
|
+
projectName,
|
|
27
|
+
packageBundlers,
|
|
28
|
+
dialect,
|
|
29
|
+
provideExample,
|
|
30
|
+
decisions
|
|
31
|
+
}) {
|
|
32
|
+
if (dialects.COMMON_JS === dialect) return buildDetailsForCommonJsProject({projectRoot, provideExample});
|
|
33
|
+
|
|
34
|
+
await fs.mkdir(`${projectRoot}/src`, {recursive: true});
|
|
35
|
+
const [bundlerResults] = await Promise.all([
|
|
36
|
+
scaffoldBundler({bundlers: packageBundlers, projectRoot, dialect, decisions, projectType: projectTypes.PACKAGE}),
|
|
37
|
+
provideExample ? await createExample(projectRoot, projectName, dialect) : Promise.resolve,
|
|
38
|
+
touch(`${projectRoot}/src/index.js`)
|
|
39
|
+
]);
|
|
40
|
+
|
|
41
|
+
return deepmerge(
|
|
42
|
+
bundlerResults,
|
|
43
|
+
{
|
|
44
|
+
dependencies: {javascript: {development: ['rimraf']}},
|
|
45
|
+
scripts: {
|
|
46
|
+
clean: `rimraf ./${defaultBuildDirectory}`,
|
|
47
|
+
prebuild: 'run-s clean',
|
|
48
|
+
build: 'npm-run-all --print-label --parallel build:*',
|
|
49
|
+
prepack: 'run-s build',
|
|
50
|
+
...provideExample && {'pregenerate:md': 'run-s build'}
|
|
51
|
+
},
|
|
52
|
+
vcsIgnore: {directories: [`/${defaultBuildDirectory}/`]},
|
|
53
|
+
buildDirectory: defaultBuildDirectory
|
|
54
|
+
}
|
|
55
|
+
);
|
|
56
|
+
}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import {promises as fs} from 'node:fs';
|
|
2
|
+
import touch from 'touch';
|
|
3
|
+
import {dialects, projectTypes} from '@form8ion/javascript-core';
|
|
4
|
+
|
|
5
|
+
import {describe, expect, it, vi} from 'vitest';
|
|
6
|
+
import any from '@travi/any';
|
|
7
|
+
import {when} from 'vitest-when';
|
|
8
|
+
|
|
9
|
+
import {scaffold as scaffoldBundler} from '../publishable/bundler/index.js';
|
|
10
|
+
import buildDetails from './build-details.js';
|
|
11
|
+
|
|
12
|
+
vi.mock('node:fs');
|
|
13
|
+
vi.mock('make-dir');
|
|
14
|
+
vi.mock('touch');
|
|
15
|
+
vi.mock('../publishable/bundler');
|
|
16
|
+
|
|
17
|
+
describe('package build details', () => {
|
|
18
|
+
const projectRoot = any.string();
|
|
19
|
+
const projectName = any.word();
|
|
20
|
+
const pathToExample = `${projectRoot}/example.js`;
|
|
21
|
+
const bundlerResults = any.simpleObject();
|
|
22
|
+
const packageBundlers = any.simpleObject();
|
|
23
|
+
const decisions = any.simpleObject();
|
|
24
|
+
|
|
25
|
+
it('should correctly define a common-js project', async () => {
|
|
26
|
+
const results = await buildDetails({
|
|
27
|
+
dialect: dialects.COMMON_JS,
|
|
28
|
+
projectRoot,
|
|
29
|
+
projectName,
|
|
30
|
+
provideExample: true
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
expect(results).toEqual({});
|
|
34
|
+
expect(fs.writeFile).toHaveBeenCalledWith(pathToExample, "const {} = require('.');\n");
|
|
35
|
+
expect(touch).toHaveBeenCalledWith(`${projectRoot}/index.js`);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it('should not create the example file for a common-js project if `provideExample` is `false`', async () => {
|
|
39
|
+
await buildDetails({
|
|
40
|
+
dialect: dialects.COMMON_JS,
|
|
41
|
+
projectRoot,
|
|
42
|
+
projectName,
|
|
43
|
+
provideExample: false
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
expect(fs.writeFile).not.toHaveBeenCalled();
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it('should define a modern-js project correctly', async () => {
|
|
50
|
+
const dialect = dialects.BABEL;
|
|
51
|
+
when(scaffoldBundler)
|
|
52
|
+
.calledWith({bundlers: packageBundlers, decisions, projectRoot, dialect, projectType: projectTypes.PACKAGE})
|
|
53
|
+
.thenResolve(bundlerResults);
|
|
54
|
+
|
|
55
|
+
const results = await buildDetails({
|
|
56
|
+
dialect,
|
|
57
|
+
projectRoot,
|
|
58
|
+
projectName,
|
|
59
|
+
packageBundlers,
|
|
60
|
+
decisions,
|
|
61
|
+
provideExample: true
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
expect(results).toEqual({
|
|
65
|
+
...bundlerResults,
|
|
66
|
+
dependencies: {javascript: {development: ['rimraf']}},
|
|
67
|
+
scripts: {
|
|
68
|
+
clean: 'rimraf ./lib',
|
|
69
|
+
prebuild: 'run-s clean',
|
|
70
|
+
build: 'npm-run-all --print-label --parallel build:*',
|
|
71
|
+
prepack: 'run-s build',
|
|
72
|
+
'pregenerate:md': 'run-s build'
|
|
73
|
+
},
|
|
74
|
+
vcsIgnore: {directories: ['/lib/']},
|
|
75
|
+
buildDirectory: 'lib'
|
|
76
|
+
});
|
|
77
|
+
expect(fs.mkdir).toHaveBeenCalledWith(`${projectRoot}/src`, {recursive: true});
|
|
78
|
+
expect(touch).toHaveBeenCalledWith(`${projectRoot}/src/index.js`);
|
|
79
|
+
expect(fs.writeFile).toHaveBeenCalledWith(pathToExample, "import {} from './lib/index.js';\n");
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
it('should not create the example file for a modern-js project when `provideExample` is `false`', async () => {
|
|
83
|
+
const dialect = dialects.BABEL;
|
|
84
|
+
when(scaffoldBundler)
|
|
85
|
+
.calledWith({bundlers: packageBundlers, decisions, projectRoot, dialect, projectType: projectTypes.PACKAGE})
|
|
86
|
+
.thenResolve(bundlerResults);
|
|
87
|
+
|
|
88
|
+
const results = await buildDetails({
|
|
89
|
+
dialect,
|
|
90
|
+
projectRoot,
|
|
91
|
+
projectName,
|
|
92
|
+
packageBundlers,
|
|
93
|
+
decisions,
|
|
94
|
+
provideExample: false
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
expect(results).toEqual({
|
|
98
|
+
...bundlerResults,
|
|
99
|
+
dependencies: {javascript: {development: ['rimraf']}},
|
|
100
|
+
scripts: {
|
|
101
|
+
clean: 'rimraf ./lib',
|
|
102
|
+
prebuild: 'run-s clean',
|
|
103
|
+
build: 'npm-run-all --print-label --parallel build:*',
|
|
104
|
+
prepack: 'run-s build'
|
|
105
|
+
},
|
|
106
|
+
vcsIgnore: {directories: ['/lib/']},
|
|
107
|
+
buildDirectory: 'lib'
|
|
108
|
+
});
|
|
109
|
+
expect(fs.writeFile).not.toHaveBeenCalled();
|
|
110
|
+
});
|
|
111
|
+
});
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import {packageManagers} from '@form8ion/javascript-core';
|
|
2
|
+
|
|
3
|
+
import buildGenerationCommand from '../../documentation/generation-command.js';
|
|
4
|
+
|
|
5
|
+
function getInstallationCommand(packageManager) {
|
|
6
|
+
if (packageManagers.NPM === packageManager) return 'npm install';
|
|
7
|
+
if (packageManagers.YARN === packageManager) return 'yarn add';
|
|
8
|
+
|
|
9
|
+
throw new Error(
|
|
10
|
+
`The ${packageManager} package manager is currently not supported. `
|
|
11
|
+
+ `Only ${Object.values(packageManagers).join(' and ')} are currently supported.`
|
|
12
|
+
);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export default function scaffoldPackageDocumentation({scope, packageName, packageManager, visibility, provideExample}) {
|
|
16
|
+
return {
|
|
17
|
+
usage: `### Installation
|
|
18
|
+
${'Private' === visibility ? `
|
|
19
|
+
:warning: this is a private package, so you will need to use an npm token with
|
|
20
|
+
access to private packages under \`@${scope}\`
|
|
21
|
+
` : ''
|
|
22
|
+
}
|
|
23
|
+
\`\`\`sh
|
|
24
|
+
$ ${getInstallationCommand(packageManager)} ${packageName}
|
|
25
|
+
\`\`\`${provideExample
|
|
26
|
+
? `
|
|
27
|
+
|
|
28
|
+
### Example
|
|
29
|
+
|
|
30
|
+
run \`${buildGenerationCommand(packageManager)}\` to inject the usage example`
|
|
31
|
+
: ''
|
|
32
|
+
}`
|
|
33
|
+
};
|
|
34
|
+
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import {packageManagers} from '@form8ion/javascript-core';
|
|
2
|
+
|
|
3
|
+
import any from '@travi/any';
|
|
4
|
+
import {describe, expect, it, vi} from 'vitest';
|
|
5
|
+
import {when} from 'vitest-when';
|
|
6
|
+
|
|
7
|
+
import buildDocumentationCommand from '../../documentation/generation-command.js';
|
|
8
|
+
import scaffoldDocumentation from './documentation.js';
|
|
9
|
+
|
|
10
|
+
vi.mock('../../documentation/generation-command.js');
|
|
11
|
+
|
|
12
|
+
describe('package documentation', () => {
|
|
13
|
+
const packageName = any.string();
|
|
14
|
+
const documentationGenerationCommand = any.string();
|
|
15
|
+
|
|
16
|
+
it('should provide `npm install` instructions for packages when the package manager is npm', () => {
|
|
17
|
+
when(buildDocumentationCommand).calledWith(packageManagers.NPM).thenReturn(documentationGenerationCommand);
|
|
18
|
+
|
|
19
|
+
const documentation = scaffoldDocumentation({
|
|
20
|
+
packageName,
|
|
21
|
+
packageManager: packageManagers.NPM,
|
|
22
|
+
provideExample: true
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
expect(documentation.usage).toEqual(`### Installation
|
|
26
|
+
|
|
27
|
+
\`\`\`sh
|
|
28
|
+
$ npm install ${packageName}
|
|
29
|
+
\`\`\`
|
|
30
|
+
|
|
31
|
+
### Example
|
|
32
|
+
|
|
33
|
+
run \`${documentationGenerationCommand}\` to inject the usage example`);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it('should not include the example section when `provideExample` is `false`', () => {
|
|
37
|
+
when(buildDocumentationCommand).calledWith(packageManagers.NPM).thenReturn(documentationGenerationCommand);
|
|
38
|
+
|
|
39
|
+
const documentation = scaffoldDocumentation({
|
|
40
|
+
packageName,
|
|
41
|
+
packageManager: packageManagers.NPM,
|
|
42
|
+
provideExample: false
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
expect(documentation.usage).toEqual(`### Installation
|
|
46
|
+
|
|
47
|
+
\`\`\`sh
|
|
48
|
+
$ npm install ${packageName}
|
|
49
|
+
\`\`\``);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it('should provide `yarn add` instructions for packages when the package manager is yarn', () => {
|
|
53
|
+
when(buildDocumentationCommand).calledWith(packageManagers.YARN).thenReturn(documentationGenerationCommand);
|
|
54
|
+
|
|
55
|
+
const documentation = scaffoldDocumentation({
|
|
56
|
+
packageName,
|
|
57
|
+
packageManager: packageManagers.YARN,
|
|
58
|
+
provideExample: true
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
expect(documentation.usage).toEqual(`### Installation
|
|
62
|
+
|
|
63
|
+
\`\`\`sh
|
|
64
|
+
$ yarn add ${packageName}
|
|
65
|
+
\`\`\`
|
|
66
|
+
|
|
67
|
+
### Example
|
|
68
|
+
|
|
69
|
+
run \`${documentationGenerationCommand}\` to inject the usage example`);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it('should throw an error for unsupported package managers', () => {
|
|
73
|
+
const packageManager = any.word();
|
|
74
|
+
|
|
75
|
+
expect(() => scaffoldDocumentation({packageName, packageManager})).toThrowError(
|
|
76
|
+
`The ${packageManager} package manager is currently not supported. `
|
|
77
|
+
+ `Only ${Object.values(packageManagers).join(' and ')} are currently supported.`
|
|
78
|
+
);
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
it('should provide an access note for private packages', () => {
|
|
82
|
+
const scope = any.word();
|
|
83
|
+
when(buildDocumentationCommand).calledWith(packageManagers.NPM).thenReturn(documentationGenerationCommand);
|
|
84
|
+
|
|
85
|
+
const documentation = scaffoldDocumentation({
|
|
86
|
+
packageName,
|
|
87
|
+
packageManager: packageManagers.NPM,
|
|
88
|
+
visibility: 'Private',
|
|
89
|
+
scope,
|
|
90
|
+
provideExample: true
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
expect(documentation.usage).toEqual(`### Installation
|
|
94
|
+
|
|
95
|
+
:warning: this is a private package, so you will need to use an npm token with
|
|
96
|
+
access to private packages under \`@${scope}\`
|
|
97
|
+
|
|
98
|
+
\`\`\`sh
|
|
99
|
+
$ npm install ${packageName}
|
|
100
|
+
\`\`\`
|
|
101
|
+
|
|
102
|
+
### Example
|
|
103
|
+
|
|
104
|
+
run \`${documentationGenerationCommand}\` to inject the usage example`);
|
|
105
|
+
});
|
|
106
|
+
});
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import {describe, expect, it, vi} from 'vitest';
|
|
2
|
+
import any from '@travi/any';
|
|
3
|
+
import {when} from 'vitest-when';
|
|
4
|
+
|
|
5
|
+
import {lift as liftPublishable} from '../publishable/index.js';
|
|
6
|
+
import lift from './lifter.js';
|
|
7
|
+
|
|
8
|
+
vi.mock('../publishable/lifter');
|
|
9
|
+
|
|
10
|
+
describe('package project-type lifter', () => {
|
|
11
|
+
it('should leverage the publishable lifter', async () => {
|
|
12
|
+
const projectRoot = any.string();
|
|
13
|
+
const packageDetails = any.simpleObject();
|
|
14
|
+
const configs = any.simpleObject();
|
|
15
|
+
const publishableResults = any.simpleObject();
|
|
16
|
+
when(liftPublishable).calledWith({projectRoot, packageDetails, configs}).thenResolve(publishableResults);
|
|
17
|
+
|
|
18
|
+
expect(await lift({projectRoot, packageDetails, configs})).toEqual(publishableResults);
|
|
19
|
+
});
|
|
20
|
+
});
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import deepmerge from 'deepmerge';
|
|
2
|
+
import {info} from '@travi/cli-messages';
|
|
3
|
+
import {dialects, mergeIntoExistingPackageJson} from '@form8ion/javascript-core';
|
|
4
|
+
|
|
5
|
+
import determinePackageAccessLevelFromProjectVisibility from '../publishable/access-level.js';
|
|
6
|
+
import {scaffold as scaffoldPublishable} from '../publishable/index.js';
|
|
7
|
+
import scaffoldPackageDocumentation from './documentation.js';
|
|
8
|
+
import buildDetails from './build-details.js';
|
|
9
|
+
|
|
10
|
+
export default async function scaffoldPackageProjectType({
|
|
11
|
+
projectRoot,
|
|
12
|
+
projectName,
|
|
13
|
+
packageName,
|
|
14
|
+
packageManager,
|
|
15
|
+
visibility,
|
|
16
|
+
scope,
|
|
17
|
+
packageBundlers,
|
|
18
|
+
decisions,
|
|
19
|
+
dialect,
|
|
20
|
+
provideExample,
|
|
21
|
+
publishRegistry
|
|
22
|
+
}) {
|
|
23
|
+
info('Scaffolding Package Details');
|
|
24
|
+
|
|
25
|
+
const packageAccessLevel = determinePackageAccessLevelFromProjectVisibility({projectVisibility: visibility});
|
|
26
|
+
const [detailsForBuild, publishableResults] = await Promise.all([
|
|
27
|
+
buildDetails({
|
|
28
|
+
projectRoot,
|
|
29
|
+
projectName,
|
|
30
|
+
packageBundlers,
|
|
31
|
+
dialect,
|
|
32
|
+
provideExample,
|
|
33
|
+
decisions
|
|
34
|
+
}),
|
|
35
|
+
scaffoldPublishable({packageName, packageAccessLevel}),
|
|
36
|
+
mergeIntoExistingPackageJson({
|
|
37
|
+
projectRoot,
|
|
38
|
+
config: {
|
|
39
|
+
files: ['example.js', ...dialects.COMMON_JS === dialect ? ['index.js'] : ['lib/', 'src/']],
|
|
40
|
+
publishConfig: {
|
|
41
|
+
access: packageAccessLevel,
|
|
42
|
+
...publishRegistry && {registry: publishRegistry}
|
|
43
|
+
},
|
|
44
|
+
sideEffects: false,
|
|
45
|
+
...dialects.BABEL === dialect && {
|
|
46
|
+
main: './lib/index.js',
|
|
47
|
+
module: './lib/index.mjs',
|
|
48
|
+
exports: {
|
|
49
|
+
module: './lib/index.mjs',
|
|
50
|
+
require: './lib/index.js',
|
|
51
|
+
import: './lib/index.mjs'
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
...dialects.ESM === dialect && {
|
|
55
|
+
main: './lib/index.js',
|
|
56
|
+
exports: './lib/index.js'
|
|
57
|
+
},
|
|
58
|
+
...dialects.TYPESCRIPT === dialect && {
|
|
59
|
+
main: './lib/index.js',
|
|
60
|
+
module: './lib/index.mjs',
|
|
61
|
+
types: './lib/index.d.ts',
|
|
62
|
+
exports: {
|
|
63
|
+
types: './lib/index.d.ts',
|
|
64
|
+
require: './lib/index.js',
|
|
65
|
+
import: './lib/index.mjs'
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
})
|
|
70
|
+
]);
|
|
71
|
+
|
|
72
|
+
return deepmerge.all([
|
|
73
|
+
publishableResults,
|
|
74
|
+
{
|
|
75
|
+
documentation: scaffoldPackageDocumentation({packageName, visibility, scope, packageManager, provideExample}),
|
|
76
|
+
nextSteps: [
|
|
77
|
+
{summary: 'Add the appropriate `save` flag to the installation instructions in the README'},
|
|
78
|
+
{summary: 'Define supported node.js versions as `engines.node` in the `package.json` file'},
|
|
79
|
+
{summary: 'Publish pre-release versions to npm until package is stable enough to publish v1.0.0'}
|
|
80
|
+
]
|
|
81
|
+
},
|
|
82
|
+
detailsForBuild
|
|
83
|
+
]);
|
|
84
|
+
}
|
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
import {dialects, mergeIntoExistingPackageJson} from '@form8ion/javascript-core';
|
|
2
|
+
|
|
3
|
+
import {beforeEach, describe, expect, it, vi} from 'vitest';
|
|
4
|
+
import any from '@travi/any';
|
|
5
|
+
import {when} from 'vitest-when';
|
|
6
|
+
|
|
7
|
+
import determinePackageAccessLevelFromProjectVisibility from '../publishable/access-level.js';
|
|
8
|
+
import {scaffold as scaffoldPublishable} from '../publishable/index.js';
|
|
9
|
+
import * as buildDetails from './build-details.js';
|
|
10
|
+
import * as documentationScaffolder from './documentation.js';
|
|
11
|
+
import scaffoldPackage from './scaffolder.js';
|
|
12
|
+
|
|
13
|
+
vi.mock('@form8ion/javascript-core');
|
|
14
|
+
vi.mock('../publishable/access-level.js');
|
|
15
|
+
vi.mock('../publishable/index.js');
|
|
16
|
+
vi.mock('./build-details.js');
|
|
17
|
+
vi.mock('./documentation.js');
|
|
18
|
+
|
|
19
|
+
describe('package project-type scaffolder', () => {
|
|
20
|
+
const projectRoot = any.string();
|
|
21
|
+
const packageBundlers = any.simpleObject();
|
|
22
|
+
const projectName = any.word();
|
|
23
|
+
const packageName = any.word();
|
|
24
|
+
const packageManager = any.word();
|
|
25
|
+
const visibility = any.word();
|
|
26
|
+
const packageAccessLevel = any.word();
|
|
27
|
+
const scope = any.word();
|
|
28
|
+
const provideExample = any.boolean();
|
|
29
|
+
const publishableResults = any.simpleObject();
|
|
30
|
+
const commonNextSteps = [
|
|
31
|
+
{summary: 'Add the appropriate `save` flag to the installation instructions in the README'},
|
|
32
|
+
{summary: 'Define supported node.js versions as `engines.node` in the `package.json` file'},
|
|
33
|
+
{summary: 'Publish pre-release versions to npm until package is stable enough to publish v1.0.0'}
|
|
34
|
+
];
|
|
35
|
+
const documentation = any.simpleObject();
|
|
36
|
+
const decisions = any.simpleObject();
|
|
37
|
+
const buildDetailsResults = any.simpleObject();
|
|
38
|
+
|
|
39
|
+
beforeEach(() => {
|
|
40
|
+
when(documentationScaffolder.default)
|
|
41
|
+
.calledWith({scope, packageName, visibility, packageManager, provideExample})
|
|
42
|
+
.thenReturn(documentation);
|
|
43
|
+
when(determinePackageAccessLevelFromProjectVisibility)
|
|
44
|
+
.calledWith({projectVisibility: visibility})
|
|
45
|
+
.thenReturn(packageAccessLevel);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it('should scaffold details specific to a modern-js package', async () => {
|
|
49
|
+
const dialect = dialects.BABEL;
|
|
50
|
+
when(scaffoldPublishable).calledWith({packageName, packageAccessLevel}).thenReturn(publishableResults);
|
|
51
|
+
when(buildDetails.default).calledWith({
|
|
52
|
+
projectRoot,
|
|
53
|
+
projectName,
|
|
54
|
+
packageBundlers,
|
|
55
|
+
dialect,
|
|
56
|
+
provideExample,
|
|
57
|
+
decisions
|
|
58
|
+
}).thenResolve(buildDetailsResults);
|
|
59
|
+
|
|
60
|
+
expect(await scaffoldPackage({
|
|
61
|
+
projectRoot,
|
|
62
|
+
projectName,
|
|
63
|
+
packageName,
|
|
64
|
+
packageManager,
|
|
65
|
+
visibility,
|
|
66
|
+
scope,
|
|
67
|
+
packageBundlers,
|
|
68
|
+
decisions,
|
|
69
|
+
dialect,
|
|
70
|
+
provideExample
|
|
71
|
+
})).toEqual({
|
|
72
|
+
...publishableResults,
|
|
73
|
+
...buildDetailsResults,
|
|
74
|
+
documentation,
|
|
75
|
+
nextSteps: commonNextSteps
|
|
76
|
+
});
|
|
77
|
+
expect(mergeIntoExistingPackageJson).toHaveBeenCalledWith({
|
|
78
|
+
projectRoot,
|
|
79
|
+
config: {
|
|
80
|
+
sideEffects: false,
|
|
81
|
+
main: './lib/index.js',
|
|
82
|
+
module: './lib/index.mjs',
|
|
83
|
+
exports: {
|
|
84
|
+
module: './lib/index.mjs',
|
|
85
|
+
require: './lib/index.js',
|
|
86
|
+
import: './lib/index.mjs'
|
|
87
|
+
},
|
|
88
|
+
files: ['example.js', 'lib/', 'src/'],
|
|
89
|
+
publishConfig: {access: packageAccessLevel}
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
it('should scaffold details specific to an esm-only package', async () => {
|
|
95
|
+
const dialect = dialects.ESM;
|
|
96
|
+
when(scaffoldPublishable).calledWith({packageName, packageAccessLevel}).thenReturn(publishableResults);
|
|
97
|
+
when(buildDetails.default).calledWith({
|
|
98
|
+
projectRoot,
|
|
99
|
+
projectName,
|
|
100
|
+
packageBundlers,
|
|
101
|
+
dialect,
|
|
102
|
+
provideExample,
|
|
103
|
+
decisions
|
|
104
|
+
}).thenResolve(buildDetailsResults);
|
|
105
|
+
|
|
106
|
+
expect(await scaffoldPackage({
|
|
107
|
+
projectRoot,
|
|
108
|
+
projectName,
|
|
109
|
+
packageName,
|
|
110
|
+
visibility,
|
|
111
|
+
dialect,
|
|
112
|
+
scope,
|
|
113
|
+
packageManager,
|
|
114
|
+
packageBundlers,
|
|
115
|
+
decisions,
|
|
116
|
+
provideExample
|
|
117
|
+
})).toEqual({
|
|
118
|
+
...publishableResults,
|
|
119
|
+
...buildDetailsResults,
|
|
120
|
+
documentation,
|
|
121
|
+
nextSteps: commonNextSteps
|
|
122
|
+
});
|
|
123
|
+
expect(mergeIntoExistingPackageJson).toHaveBeenCalledWith({
|
|
124
|
+
projectRoot,
|
|
125
|
+
config: {
|
|
126
|
+
main: './lib/index.js',
|
|
127
|
+
exports: './lib/index.js',
|
|
128
|
+
files: ['example.js', 'lib/', 'src/'],
|
|
129
|
+
sideEffects: false,
|
|
130
|
+
publishConfig: {access: packageAccessLevel}
|
|
131
|
+
}
|
|
132
|
+
});
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
it('should scaffold details specific to a typescript package', async () => {
|
|
136
|
+
const dialect = dialects.TYPESCRIPT;
|
|
137
|
+
when(scaffoldPublishable).calledWith({packageName, packageAccessLevel}).thenReturn(publishableResults);
|
|
138
|
+
when(buildDetails.default).calledWith({
|
|
139
|
+
projectRoot,
|
|
140
|
+
projectName,
|
|
141
|
+
packageBundlers,
|
|
142
|
+
dialect,
|
|
143
|
+
provideExample,
|
|
144
|
+
decisions
|
|
145
|
+
}).thenResolve(buildDetailsResults);
|
|
146
|
+
|
|
147
|
+
expect(await scaffoldPackage({
|
|
148
|
+
projectRoot,
|
|
149
|
+
projectName,
|
|
150
|
+
packageName,
|
|
151
|
+
packageManager,
|
|
152
|
+
visibility,
|
|
153
|
+
scope,
|
|
154
|
+
packageBundlers,
|
|
155
|
+
decisions,
|
|
156
|
+
dialect,
|
|
157
|
+
provideExample
|
|
158
|
+
})).toEqual({
|
|
159
|
+
...publishableResults,
|
|
160
|
+
...buildDetailsResults,
|
|
161
|
+
documentation,
|
|
162
|
+
nextSteps: commonNextSteps
|
|
163
|
+
});
|
|
164
|
+
expect(mergeIntoExistingPackageJson).toHaveBeenCalledWith({
|
|
165
|
+
projectRoot,
|
|
166
|
+
config: {
|
|
167
|
+
sideEffects: false,
|
|
168
|
+
main: './lib/index.js',
|
|
169
|
+
module: './lib/index.mjs',
|
|
170
|
+
types: './lib/index.d.ts',
|
|
171
|
+
exports: {
|
|
172
|
+
types: './lib/index.d.ts',
|
|
173
|
+
require: './lib/index.js',
|
|
174
|
+
import: './lib/index.mjs'
|
|
175
|
+
},
|
|
176
|
+
files: ['example.js', 'lib/', 'src/'],
|
|
177
|
+
publishConfig: {access: packageAccessLevel}
|
|
178
|
+
}
|
|
179
|
+
});
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
it('should not include build details when the project will not be scaffolded', async () => {
|
|
183
|
+
const dialect = dialects.COMMON_JS;
|
|
184
|
+
when(scaffoldPublishable).calledWith({packageName, packageAccessLevel}).thenReturn(publishableResults);
|
|
185
|
+
when(buildDetails.default).calledWith({
|
|
186
|
+
projectRoot,
|
|
187
|
+
projectName,
|
|
188
|
+
packageBundlers,
|
|
189
|
+
dialect,
|
|
190
|
+
provideExample,
|
|
191
|
+
decisions
|
|
192
|
+
}).thenResolve(buildDetailsResults);
|
|
193
|
+
|
|
194
|
+
expect(await scaffoldPackage({
|
|
195
|
+
projectRoot,
|
|
196
|
+
packageName,
|
|
197
|
+
projectName,
|
|
198
|
+
packageManager,
|
|
199
|
+
visibility,
|
|
200
|
+
scope,
|
|
201
|
+
decisions,
|
|
202
|
+
packageBundlers,
|
|
203
|
+
dialect,
|
|
204
|
+
provideExample
|
|
205
|
+
})).toEqual({
|
|
206
|
+
...publishableResults,
|
|
207
|
+
...buildDetailsResults,
|
|
208
|
+
documentation,
|
|
209
|
+
nextSteps: commonNextSteps
|
|
210
|
+
});
|
|
211
|
+
expect(mergeIntoExistingPackageJson).toHaveBeenCalledWith({
|
|
212
|
+
projectRoot,
|
|
213
|
+
config: {
|
|
214
|
+
files: ['example.js', 'index.js'],
|
|
215
|
+
publishConfig: {access: packageAccessLevel},
|
|
216
|
+
sideEffects: false
|
|
217
|
+
}
|
|
218
|
+
});
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
it('should define the registry to publish to when provided', async () => {
|
|
222
|
+
const publishRegistry = any.url();
|
|
223
|
+
const dialect = dialects.BABEL;
|
|
224
|
+
when(scaffoldPublishable).calledWith({packageName, packageAccessLevel}).thenReturn(publishableResults);
|
|
225
|
+
when(buildDetails.default).calledWith({
|
|
226
|
+
projectRoot,
|
|
227
|
+
projectName,
|
|
228
|
+
packageBundlers,
|
|
229
|
+
dialect,
|
|
230
|
+
provideExample,
|
|
231
|
+
decisions
|
|
232
|
+
}).thenResolve(buildDetailsResults);
|
|
233
|
+
|
|
234
|
+
await scaffoldPackage({
|
|
235
|
+
projectRoot,
|
|
236
|
+
packageName,
|
|
237
|
+
projectName,
|
|
238
|
+
packageManager,
|
|
239
|
+
visibility,
|
|
240
|
+
scope,
|
|
241
|
+
decisions,
|
|
242
|
+
publishRegistry,
|
|
243
|
+
dialect,
|
|
244
|
+
provideExample,
|
|
245
|
+
packageBundlers
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
expect(mergeIntoExistingPackageJson).toHaveBeenCalledWith({
|
|
249
|
+
projectRoot,
|
|
250
|
+
config: {
|
|
251
|
+
sideEffects: false,
|
|
252
|
+
main: './lib/index.js',
|
|
253
|
+
module: './lib/index.mjs',
|
|
254
|
+
exports: {
|
|
255
|
+
module: './lib/index.mjs',
|
|
256
|
+
require: './lib/index.js',
|
|
257
|
+
import: './lib/index.mjs'
|
|
258
|
+
},
|
|
259
|
+
files: ['example.js', 'lib/', 'src/'],
|
|
260
|
+
publishConfig: {
|
|
261
|
+
access: packageAccessLevel,
|
|
262
|
+
registry: publishRegistry
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
});
|
|
266
|
+
});
|
|
267
|
+
});
|