@40q/40q-cli 1.0.0 → 1.0.2

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/.cliroot ADDED
File without changes
package/Readme.md CHANGED
@@ -19,4 +19,18 @@ Setup is intended to scaffold tools configurations.
19
19
  ```
20
20
 
21
21
  Supported Tools:
22
- * eslint
22
+ * eslint
23
+
24
+ ### Codegen
25
+ Codegen generates code for you.
26
+
27
+ ```
28
+ 40q codegen [type] [template]
29
+ ```
30
+
31
+ Supported Types:
32
+ * block: generates code for Gutenberg blocks
33
+
34
+ Templates are optional, if empty, it creates an example boilerplate for a Gutenberg block.
35
+
36
+ For supported templates, run: `40q codegen block --help`
package/dist/index.js ADDED
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ const _40qCli_1 = require("./src/40qCli");
5
+ _40qCli_1.q40Cli.init();
@@ -0,0 +1,39 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.styles = exports.save = exports.edit = exports.attributes = exports.category = exports.title = exports.name = void 0;
4
+ const i18n_1 = require("@wordpress/i18n");
5
+ const components_1 = require("@wordpress/components");
6
+ const block_editor_1 = require("@wordpress/block-editor");
7
+ /* Block name */
8
+ exports.name = "by40q/section-header";
9
+ /* Block title */
10
+ exports.title = (0, i18n_1.__)("Section Header", "40q");
11
+ /* Block category */
12
+ exports.category = "text";
13
+ /* Block attributes */
14
+ exports.attributes = {
15
+ title: {
16
+ type: "string",
17
+ default: "40Q",
18
+ },
19
+ };
20
+ /* Block edit */
21
+ const edit = ({ attributes, setAttributes }) => {
22
+ const { title } = attributes;
23
+ const blockProps = (0, block_editor_1.useBlockProps)();
24
+ return (<>
25
+ <block_editor_1.InspectorControls>
26
+ <components_1.PanelBody title={(0, i18n_1.__)("Section Header Settings")} initialOpen></components_1.PanelBody>
27
+ </block_editor_1.InspectorControls>
28
+
29
+ <div {...blockProps}>
30
+ <block_editor_1.RichText tagName="h2" placeholder={(0, i18n_1.__)("40Q")} value={title} onChange={(title) => setAttributes({ title })}/>
31
+ </div>
32
+ </>);
33
+ };
34
+ exports.edit = edit;
35
+ /* Block save */
36
+ const save = ({ attributes }) => <></>;
37
+ exports.save = save;
38
+ /* Block styles */
39
+ exports.styles = [];
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.q40Cli = void 0;
7
+ const yargs_1 = __importDefault(require("yargs"));
8
+ const helpers_1 = require("yargs/helpers");
9
+ const Setup_command_1 = require("./commands/Setup/Setup.command");
10
+ const Codegen_command_1 = require("./commands/Codegen/Codegen.command");
11
+ class q40Cli {
12
+ static init() {
13
+ (0, yargs_1.default)((0, helpers_1.hideBin)(process.argv))
14
+ .command('setup [tool]', 'Setup a tool', Setup_command_1.SetupCommand.builder, Setup_command_1.SetupCommand.handler)
15
+ .command('codegen [type] [template]', 'Generate code from a template', Codegen_command_1.CodegenCommand.builder, Codegen_command_1.CodegenCommand.handler)
16
+ .help().argv;
17
+ }
18
+ }
19
+ exports.q40Cli = q40Cli;
@@ -0,0 +1,61 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ var __importDefault = (this && this.__importDefault) || function (mod) {
12
+ return (mod && mod.__esModule) ? mod : { "default": mod };
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.CodegenCommand = void 0;
16
+ const inquirer_1 = __importDefault(require("inquirer"));
17
+ const GenerateBlock_1 = require("./Generators/GenerateBlock");
18
+ const Codegen_types_1 = require("./Codegen.types");
19
+ class CodegenCommand {
20
+ static builder(yargs) {
21
+ yargs.positional('type', {
22
+ describe: 'Type of code piece to generate',
23
+ choices: Codegen_types_1.typeChoices,
24
+ demandOption: true,
25
+ type: 'string',
26
+ });
27
+ yargs.positional('template', {
28
+ describe: 'Template to use for code generation',
29
+ choices: Codegen_types_1.templateChoices,
30
+ demandOption: false,
31
+ type: 'string',
32
+ });
33
+ }
34
+ static handler(argv) {
35
+ var _a;
36
+ return __awaiter(this, void 0, void 0, function* () {
37
+ const type = argv.type;
38
+ const template = ((_a = argv.template) !== null && _a !== void 0 ? _a : 'default');
39
+ if (type === 'block') {
40
+ const answers = yield CodegenCommand.prompt([
41
+ {
42
+ type: 'input',
43
+ name: 'name',
44
+ message: 'Please enter a name for the block:',
45
+ default: 'section-header',
46
+ },
47
+ ], template === 'default');
48
+ const name = (answers === null || answers === void 0 ? void 0 : answers.name) || null;
49
+ return GenerateBlock_1.GenerateBlock.run(template, name);
50
+ }
51
+ });
52
+ }
53
+ static prompt(prompts, condition) {
54
+ return __awaiter(this, void 0, void 0, function* () {
55
+ if (!condition)
56
+ return null;
57
+ return yield inquirer_1.default.prompt(prompts);
58
+ });
59
+ }
60
+ }
61
+ exports.CodegenCommand = CodegenCommand;
@@ -0,0 +1,5 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.templateChoices = exports.typeChoices = void 0;
4
+ exports.typeChoices = ['block'];
5
+ exports.templateChoices = ['section-header'];
@@ -0,0 +1,49 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ var __importDefault = (this && this.__importDefault) || function (mod) {
26
+ return (mod && mod.__esModule) ? mod : { "default": mod };
27
+ };
28
+ Object.defineProperty(exports, "__esModule", { value: true });
29
+ exports.GenerateBlock = void 0;
30
+ const child_process_1 = require("child_process");
31
+ const fs = __importStar(require("fs"));
32
+ const path_1 = __importDefault(require("path"));
33
+ const cliRoot_1 = require("../../../lib/cliRoot");
34
+ class GenerateBlock {
35
+ static run(template, name) {
36
+ var _a;
37
+ const title = (_a = name === null || name === void 0 ? void 0 : name.split('-').map((word) => {
38
+ return word.charAt(0).toUpperCase() + word.slice(1);
39
+ }).join(' ')) !== null && _a !== void 0 ? _a : '';
40
+ const dirAndFileName = name || template;
41
+ (0, child_process_1.execSync)(`mkdir -p resources/scripts/editor/blocks/${dirAndFileName}`);
42
+ const cliRoot = (0, cliRoot_1.findCliRoot)(__dirname);
43
+ const templateContent = fs.readFileSync(path_1.default.join(cliRoot, 'templates/blocks/', `${template}.txt`), 'utf8');
44
+ fs.writeFileSync(path_1.default.join(process.cwd(), `resources/scripts/editor/blocks/${dirAndFileName}/${dirAndFileName}.tsx`), templateContent
45
+ .replace(/{{name}}/g, name !== null && name !== void 0 ? name : '')
46
+ .replace(/{{title}}/g, title));
47
+ }
48
+ }
49
+ exports.GenerateBlock = GenerateBlock;
@@ -0,0 +1,8 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Command = void 0;
4
+ class Command {
5
+ static builder(yargs) { }
6
+ static handler(argv) { }
7
+ }
8
+ exports.Command = Command;
@@ -0,0 +1,53 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SetupCommand = void 0;
4
+ const child_process_1 = require("child_process");
5
+ const eslintrc = `
6
+ {
7
+ "extends": ["@40q/eslint-config"]
8
+ }
9
+ `;
10
+ const huskyrc = `
11
+ {
12
+ "hooks": {
13
+ "pre-commit": "lint-staged"
14
+ }
15
+ }
16
+ `;
17
+ const lintstagedrc = `
18
+ {
19
+ "*.{js,jsx,ts,tsx}": ["eslint --fix"]
20
+ }
21
+ `;
22
+ class SetupCommand {
23
+ static builder(yargs) {
24
+ yargs.positional('tool', {
25
+ describe: 'Name of the tool to setup',
26
+ type: 'string',
27
+ });
28
+ }
29
+ static handler(argv) {
30
+ SetupCommand.setupTool(argv.tool);
31
+ }
32
+ static setupTool(tool) {
33
+ switch (tool) {
34
+ case 'eslint':
35
+ this.setupEslint();
36
+ break;
37
+ default:
38
+ console.error(`Unknown tool: ${tool}. Please specify a supported tool.`);
39
+ }
40
+ }
41
+ static setupEslint() {
42
+ console.log('Installing dependencies...');
43
+ (0, child_process_1.execSync)('yarn add --dev eslint @40q/eslint-config husky lint-staged');
44
+ console.log('Creating .eslintrc.json...');
45
+ (0, child_process_1.execSync)(`echo '${eslintrc}' > .eslintrc.json`);
46
+ console.log('Creating .huskyrc.json...');
47
+ (0, child_process_1.execSync)(`echo '${huskyrc}' > .huskyrc.json`);
48
+ console.log('Creating .lintstagedrc.json...');
49
+ (0, child_process_1.execSync)(`echo '${lintstagedrc}' > .lintstagedrc.json`);
50
+ console.log('Done!');
51
+ }
52
+ }
53
+ exports.SetupCommand = SetupCommand;
@@ -0,0 +1,53 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SetupCommand = void 0;
4
+ const child_process_1 = require("child_process");
5
+ const eslintrc = `
6
+ {
7
+ "extends": ["@40q/eslint-config"]
8
+ }
9
+ `;
10
+ const huskyrc = `
11
+ {
12
+ "hooks": {
13
+ "pre-commit": "lint-staged"
14
+ }
15
+ }
16
+ `;
17
+ const lintstagedrc = `
18
+ {
19
+ "*.{js,jsx,ts,tsx}": ["eslint --fix"]
20
+ }
21
+ `;
22
+ class SetupCommand {
23
+ static builder(yargs) {
24
+ yargs.positional('tool', {
25
+ describe: 'Name of the tool to setup',
26
+ type: 'string',
27
+ });
28
+ }
29
+ static handler(argv) {
30
+ SetupCommand.setupTool(argv.tool);
31
+ }
32
+ static setupTool(tool) {
33
+ switch (tool) {
34
+ case 'eslint':
35
+ this.setupEslint();
36
+ break;
37
+ default:
38
+ console.error(`Unknown tool: ${tool}. Please specify a supported tool.`);
39
+ }
40
+ }
41
+ static setupEslint() {
42
+ console.log('Installing dependencies...');
43
+ (0, child_process_1.execSync)('yarn add --dev eslint @40q/eslint-config husky lint-staged');
44
+ console.log('Creating .eslintrc.json...');
45
+ (0, child_process_1.execSync)(`echo '${eslintrc}' > .eslintrc.json`);
46
+ console.log('Creating .huskyrc.json...');
47
+ (0, child_process_1.execSync)(`echo '${huskyrc}' > .huskyrc.json`);
48
+ console.log('Creating .lintstagedrc.json...');
49
+ (0, child_process_1.execSync)(`echo '${lintstagedrc}' > .lintstagedrc.json`);
50
+ console.log('Done!');
51
+ }
52
+ }
53
+ exports.SetupCommand = SetupCommand;
@@ -0,0 +1,39 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ Object.defineProperty(exports, "__esModule", { value: true });
26
+ exports.findCliRoot = void 0;
27
+ const fs = __importStar(require("fs"));
28
+ const path = __importStar(require("path"));
29
+ function findCliRoot(startDir) {
30
+ let currentDir = startDir;
31
+ while (currentDir !== path.parse(currentDir).root) {
32
+ if (fs.existsSync(path.join(currentDir, '.cliroot'))) {
33
+ return currentDir;
34
+ }
35
+ currentDir = path.dirname(currentDir);
36
+ }
37
+ throw new Error('CLI root directory with .cliroot marker not found.');
38
+ }
39
+ exports.findCliRoot = findCliRoot;
package/package.json CHANGED
@@ -1,12 +1,13 @@
1
1
  {
2
2
  "name": "@40q/40q-cli",
3
- "version": "1.0.0",
3
+ "version": "1.0.2",
4
4
  "description": "40q CLI tool",
5
5
  "main": "index.js",
6
6
  "bin": {
7
7
  "40q": "./dist/index.js"
8
8
  },
9
9
  "dependencies": {
10
+ "inquirer": "^8.0.0",
10
11
  "yargs": "^17.7.2"
11
12
  },
12
13
  "peerDependencies": {
@@ -14,7 +15,7 @@
14
15
  },
15
16
  "scripts": {
16
17
  "build": "tsc",
17
- "build-publish": "yarn build && yarn publish --access public"
18
+ "deploy": "yarn build && yarn publish --access public"
18
19
  },
19
20
  "author": "40q <info@40q.agency> (https://40q.agency/)",
20
21
  "license": "ISC",
@@ -23,6 +24,7 @@
23
24
  "url": "https://github.com/40Q/40q-cli.git"
24
25
  },
25
26
  "devDependencies": {
27
+ "@types/inquirer": "^8.0.0",
26
28
  "@types/node": "^20.8.7",
27
29
  "@types/yargs": "^17.0.29",
28
30
  "ts-node": "^10.9.1",
package/src/40qCli.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import yargs from 'yargs';
2
2
  import { hideBin } from 'yargs/helpers';
3
- import { SetupCommand } from './commands/Setup.command';
3
+ import { SetupCommand } from './commands/Setup/Setup.command';
4
+ import { CodegenCommand } from './commands/Codegen/Codegen.command';
4
5
 
5
6
  export class q40Cli {
6
7
  static init() {
@@ -11,6 +12,12 @@ export class q40Cli {
11
12
  SetupCommand.builder,
12
13
  SetupCommand.handler
13
14
  )
15
+ .command(
16
+ 'codegen [type] [template]',
17
+ 'Generate code from a template',
18
+ CodegenCommand.builder,
19
+ CodegenCommand.handler
20
+ )
14
21
  .help().argv;
15
22
  }
16
23
  }
@@ -0,0 +1,52 @@
1
+ import { ArgumentsCamelCase, Argv } from 'yargs';
2
+ import { Command } from '../Command';
3
+ import inquirer, { QuestionCollection } from 'inquirer';
4
+ import { GenerateBlock } from './Generators/GenerateBlock';
5
+ import { Templates, Types, templateChoices, typeChoices } from './Codegen.types';
6
+
7
+ export class CodegenCommand implements Command {
8
+ static builder(yargs: Argv<{}>) {
9
+ yargs.positional('type', {
10
+ describe: 'Type of code piece to generate',
11
+ choices: typeChoices,
12
+ demandOption: true,
13
+ type: 'string',
14
+ });
15
+
16
+ yargs.positional('template', {
17
+ describe: 'Template to use for code generation',
18
+ choices: templateChoices,
19
+ demandOption: false,
20
+ type: 'string',
21
+ });
22
+ }
23
+
24
+ static async handler(argv: ArgumentsCamelCase<{}>) {
25
+ const type = argv.type as Types;
26
+ const template = (argv.template ?? 'default') as Templates;
27
+
28
+ if (type === 'block') {
29
+ const answers = await CodegenCommand.prompt(
30
+ [
31
+ {
32
+ type: 'input',
33
+ name: 'name',
34
+ message: 'Please enter a name for the block:',
35
+ default: 'section-header',
36
+ },
37
+ ],
38
+ template === 'default'
39
+ );
40
+
41
+ const name = (answers?.name as string) || null;
42
+
43
+ return GenerateBlock.run(template, name);
44
+ }
45
+ }
46
+
47
+ static async prompt(prompts: QuestionCollection, condition: boolean) {
48
+ if (!condition) return null;
49
+
50
+ return await inquirer.prompt(prompts);
51
+ }
52
+ }
@@ -0,0 +1,5 @@
1
+ export const typeChoices = ['block'] as const;
2
+ export type Types = (typeof typeChoices)[number];
3
+
4
+ export const templateChoices = ['section-header'] as const;
5
+ export type Templates = (typeof templateChoices)[number] | 'default';
@@ -0,0 +1,36 @@
1
+ import { execSync } from 'child_process';
2
+ import * as fs from 'fs';
3
+ import path from 'path';
4
+ import { Templates } from '../Codegen.types';
5
+ import { findCliRoot } from '../../../lib/cliRoot';
6
+
7
+ export class GenerateBlock {
8
+ static run(template: Templates, name: string | null) {
9
+ const title =
10
+ name
11
+ ?.split('-')
12
+ .map((word) => {
13
+ return word.charAt(0).toUpperCase() + word.slice(1);
14
+ })
15
+ .join(' ') ?? '';
16
+ const dirAndFileName = name || template;
17
+
18
+ execSync(`mkdir -p resources/scripts/editor/blocks/${dirAndFileName}`);
19
+
20
+ const cliRoot = findCliRoot(__dirname);
21
+ const templateContent = fs.readFileSync(
22
+ path.join(cliRoot, 'templates/blocks/', `${template}.txt`),
23
+ 'utf8'
24
+ );
25
+
26
+ fs.writeFileSync(
27
+ path.join(
28
+ process.cwd(),
29
+ `resources/scripts/editor/blocks/${dirAndFileName}/${dirAndFileName}.tsx`
30
+ ),
31
+ templateContent
32
+ .replace(/{{name}}/g, name ?? '')
33
+ .replace(/{{title}}/g, title)
34
+ );
35
+ }
36
+ }
@@ -1,6 +1,6 @@
1
1
  import { ArgumentsCamelCase, Argv } from 'yargs';
2
2
  import { execSync } from 'child_process';
3
- import { Command } from './Command';
3
+ import { Command } from '../Command';
4
4
 
5
5
  const eslintrc = `
6
6
  {
@@ -0,0 +1,16 @@
1
+ import * as fs from 'fs';
2
+ import * as path from 'path';
3
+
4
+ export function findCliRoot(startDir: string): string {
5
+ let currentDir = startDir;
6
+
7
+ while (currentDir !== path.parse(currentDir).root) {
8
+ if (fs.existsSync(path.join(currentDir, '.cliroot'))) {
9
+ return currentDir;
10
+ }
11
+
12
+ currentDir = path.dirname(currentDir);
13
+ }
14
+
15
+ throw new Error('CLI root directory with .cliroot marker not found.');
16
+ }
@@ -0,0 +1,54 @@
1
+ import { __ } from "@wordpress/i18n";
2
+ import { PanelBody } from "@wordpress/components";
3
+ import {
4
+ InspectorControls,
5
+ RichText,
6
+ useBlockProps,
7
+ } from "@wordpress/block-editor";
8
+
9
+ /* Block name */
10
+ export const name = "by40q/{{name}}";
11
+
12
+ /* Block title */
13
+ export const title = __("{{title}}", "40q");
14
+
15
+ /* Block category */
16
+ export const category = "text";
17
+
18
+ /* Block attributes */
19
+ export const attributes = {
20
+ title: {
21
+ type: "string",
22
+ default: "40Q",
23
+ },
24
+ };
25
+
26
+ /* Block edit */
27
+ export const edit = ({ attributes, setAttributes }) => {
28
+ const { title } = attributes;
29
+
30
+ const blockProps = useBlockProps();
31
+
32
+ return (
33
+ <>
34
+ <InspectorControls>
35
+ <PanelBody title={__("{{title}} Settings")} initialOpen></PanelBody>
36
+ </InspectorControls>
37
+
38
+ <div {...blockProps}>
39
+ <RichText
40
+ tagName="h2"
41
+ placeholder={__("40Q")}
42
+ value={title}
43
+ onChange={(title) => setAttributes({ title })}
44
+ />
45
+ </div>
46
+ </>
47
+ );
48
+ };
49
+
50
+ /* Block save */
51
+ export const save = () => <></>;
52
+
53
+ /* Block styles */
54
+ export const styles = [];
package/tsconfig.json CHANGED
@@ -1,7 +1,8 @@
1
1
  {
2
2
  "compilerOptions": {
3
3
  "target": "es2016",
4
- "module": "commonjs",
4
+ "module": "CommonJS",
5
+ "moduleResolution": "node",
5
6
  "esModuleInterop": true,
6
7
  "forceConsistentCasingInFileNames": true,
7
8
  "strict": true,