@lazarv/create-react-server 0.0.0-experimental-d003259-20250211-2495688e

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (95) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +31 -0
  3. package/generator.mjs +195 -0
  4. package/globals.d.ts +59 -0
  5. package/index.mjs +87 -0
  6. package/launch.mjs +136 -0
  7. package/lib/code-merge.mjs +246 -0
  8. package/lib/dynamic-checkbox.mjs +259 -0
  9. package/lib/files.mjs +6 -0
  10. package/lib/formatter.mjs +16 -0
  11. package/lib/generate-name.mjs +220 -0
  12. package/lib/theme.mjs +56 -0
  13. package/logo.mjs +3 -0
  14. package/package.json +26 -0
  15. package/steps/alias.mjs +50 -0
  16. package/steps/authentication.mjs +62 -0
  17. package/steps/database.mjs +56 -0
  18. package/steps/deploy.mjs +151 -0
  19. package/steps/features.mjs +415 -0
  20. package/steps/host.mjs +40 -0
  21. package/steps/index.mjs +33 -0
  22. package/steps/integrations.mjs +104 -0
  23. package/steps/name.mjs +69 -0
  24. package/steps/package.mjs +104 -0
  25. package/steps/port.mjs +42 -0
  26. package/steps/preset.mjs +188 -0
  27. package/steps/state-management.mjs +68 -0
  28. package/steps/third-party.mjs +19 -0
  29. package/steps/ui.mjs +87 -0
  30. package/templates/.dockerignore +5 -0
  31. package/templates/.gitignore.template +19 -0
  32. package/templates/.prettierignore +3 -0
  33. package/templates/.prettierrc +11 -0
  34. package/templates/Dockerfile.npm +49 -0
  35. package/templates/Dockerfile.pnpm +71 -0
  36. package/templates/Dockerfile.yarn +71 -0
  37. package/templates/README.docker.md +15 -0
  38. package/templates/README.md +35 -0
  39. package/templates/blank/package.json +6 -0
  40. package/templates/blank/src/App.jsx +5 -0
  41. package/templates/blank-ts/package.json +6 -0
  42. package/templates/blank-ts/src/App.tsx +5 -0
  43. package/templates/eslint.config.template.mjs +98 -0
  44. package/templates/get-started/package.json +6 -0
  45. package/templates/get-started/src/App.jsx +60 -0
  46. package/templates/get-started/src/Button.jsx +14 -0
  47. package/templates/get-started/src/Confetti.jsx +19 -0
  48. package/templates/get-started/src/global.css +3 -0
  49. package/templates/get-started-ts/globals.d.ts +3 -0
  50. package/templates/get-started-ts/package.json +6 -0
  51. package/templates/get-started-ts/src/App.tsx +66 -0
  52. package/templates/get-started-ts/src/Button.tsx +18 -0
  53. package/templates/get-started-ts/src/Confetti.tsx +19 -0
  54. package/templates/get-started-ts/src/global.css +3 -0
  55. package/templates/nextjs/globals.d.ts +3 -0
  56. package/templates/nextjs/react-server.config.json +9 -0
  57. package/templates/nextjs/src/app/layout.tsx +38 -0
  58. package/templates/nextjs/src/app/page.tsx +33 -0
  59. package/templates/nextjs/src/components/Button.tsx +18 -0
  60. package/templates/nextjs/src/components/Confetti.tsx +19 -0
  61. package/templates/nextjs/src/global.css +3 -0
  62. package/templates/package.css.json +5 -0
  63. package/templates/package.eslint.json +19 -0
  64. package/templates/package.eslint.ts.json +7 -0
  65. package/templates/package.json +12 -0
  66. package/templates/package.lightningcss.json +6 -0
  67. package/templates/package.prettier.json +8 -0
  68. package/templates/package.react-compiler.json +7 -0
  69. package/templates/package.swc.json +5 -0
  70. package/templates/package.tailwind.json +7 -0
  71. package/templates/package.ts.json +11 -0
  72. package/templates/postcss.config.mjs +6 -0
  73. package/templates/router/globals.d.ts +3 -0
  74. package/templates/router/react-server.config.json +19 -0
  75. package/templates/router/src/app/@content/about.tsx +98 -0
  76. package/templates/router/src/app/@content/index.tsx +33 -0
  77. package/templates/router/src/app/layout.tsx +44 -0
  78. package/templates/router/src/app/page.tsx +17 -0
  79. package/templates/router/src/components/Button.tsx +18 -0
  80. package/templates/router/src/components/Confetti.tsx +19 -0
  81. package/templates/router/src/components/Navigation.tsx +31 -0
  82. package/templates/router/src/global.css +3 -0
  83. package/templates/shared/public/github.svg +4 -0
  84. package/templates/shared/public/react-server.svg +51 -0
  85. package/templates/tailwind.config.mjs +6 -0
  86. package/templates/tsconfig.css.json +9 -0
  87. package/templates/tsconfig.template.json +15 -0
  88. package/templates/vite.config.lightningcss.mjs +15 -0
  89. package/templates/vite.config.lightningcss.ts +15 -0
  90. package/templates/vite.config.react-compiler.mjs +19 -0
  91. package/templates/vite.config.react-compiler.ts +19 -0
  92. package/templates/vite.config.swc.mjs +6 -0
  93. package/templates/vite.config.swc.ts +6 -0
  94. package/templates/vite.config.ts +3 -0
  95. package/wizard.mjs +122 -0
@@ -0,0 +1,246 @@
1
+ import { Project, ts } from "ts-morph";
2
+
3
+ import { format } from "./formatter.mjs";
4
+
5
+ export async function mergeCodeFiles(...fileContents) {
6
+ const project = new Project({
7
+ useInMemoryFileSystem: true,
8
+ compilerOptions: {
9
+ allowJs: true,
10
+ checkJs: false,
11
+ esModuleInterop: true,
12
+ },
13
+ });
14
+
15
+ const mergedSourceFile = project.createSourceFile("mergedFile.ts", "", {
16
+ overwrite: true,
17
+ });
18
+
19
+ const importDeclarations = [];
20
+ const otherStatementsMap = new Map();
21
+
22
+ fileContents.forEach((content, index) => {
23
+ const sourceFile = project.createSourceFile(`file${index}.ts`, content, {
24
+ overwrite: true,
25
+ });
26
+
27
+ sourceFile.getImportDeclarations().forEach((importDecl) => {
28
+ importDeclarations.push(importDecl);
29
+ });
30
+
31
+ sourceFile.getStatements().forEach((stmt) => {
32
+ if (!stmt.isKind(ts.SyntaxKind.ImportDeclaration)) {
33
+ const key = getStatementKey(stmt);
34
+ if (!otherStatementsMap.has(key)) {
35
+ otherStatementsMap.set(key, stmt);
36
+ } else {
37
+ const existingStmt = otherStatementsMap.get(key);
38
+ mergeNodes(existingStmt, stmt);
39
+ }
40
+ }
41
+ });
42
+ });
43
+
44
+ const groupedImports = groupAndSortImports(importDeclarations);
45
+ groupedImports.forEach((importDecl) => {
46
+ mergedSourceFile.addImportDeclaration({
47
+ moduleSpecifier: importDecl.getModuleSpecifierValue(),
48
+ defaultImport: importDecl.getDefaultImport()?.getText(),
49
+ namedImports: importDecl.getNamedImports().map((ni) => ni.getText()),
50
+ });
51
+ });
52
+
53
+ otherStatementsMap.forEach((stmt) => {
54
+ mergedSourceFile.addStatements(stmt.getFullText());
55
+ });
56
+
57
+ const mergedCode = mergedSourceFile.getFullText();
58
+ return format(mergedCode, "typescript");
59
+ }
60
+
61
+ function getStatementKey(stmt) {
62
+ if (stmt.isKind(ts.SyntaxKind.VariableStatement)) {
63
+ const declarations = stmt.getDeclarationList().getDeclarations();
64
+ return declarations.map((decl) => decl.getName()).join(",");
65
+ } else if (stmt.isKind(ts.SyntaxKind.FunctionDeclaration)) {
66
+ return `Function:${stmt.getName()}`;
67
+ } else if (stmt.isKind(ts.SyntaxKind.ClassDeclaration)) {
68
+ return `Class:${stmt.getName()}`;
69
+ } else if (stmt.isKind(ts.SyntaxKind.ExpressionStatement)) {
70
+ const expression = stmt.getExpression();
71
+ return `Expression:${expression.getText()}`;
72
+ } else if (stmt.isKind(ts.SyntaxKind.ExportAssignment)) {
73
+ return `ExportAssignment`;
74
+ }
75
+ return `Kind:${stmt.getKindName()}:${stmt.getText()}`;
76
+ }
77
+
78
+ function mergeNodes(target, source) {
79
+ if (
80
+ ts.isObjectLiteralExpression(target.compilerNode) &&
81
+ ts.isObjectLiteralExpression(source.compilerNode)
82
+ ) {
83
+ mergeObjectLiterals(target, source);
84
+ } else if (target.getChildCount() > 0 && source.getChildCount() > 0) {
85
+ const targetChildren = target.getChildren();
86
+ const sourceChildren = source.getChildren();
87
+ const length = Math.max(targetChildren.length, sourceChildren.length);
88
+
89
+ for (let i = 0; i < length; i++) {
90
+ const targetChild = targetChildren[i];
91
+ const sourceChild = sourceChildren[i];
92
+
93
+ if (targetChild && sourceChild) {
94
+ mergeNodes(targetChild, sourceChild);
95
+ } else if (sourceChild) {
96
+ target.addChildText(sourceChild.getFullText());
97
+ }
98
+ }
99
+ } else {
100
+ target.replaceWithText(source.getFullText());
101
+ }
102
+ }
103
+
104
+ function mergeObjectLiterals(target, source) {
105
+ const targetProperties = target.getProperties();
106
+ const sourceProperties = source.getProperties();
107
+
108
+ const targetPropertyMap = new Map();
109
+ targetProperties.forEach((prop) => {
110
+ const name = prop.getName();
111
+ if (name) {
112
+ targetPropertyMap.set(name, prop);
113
+ }
114
+ });
115
+
116
+ sourceProperties.forEach((sourceProp) => {
117
+ const name = sourceProp.getName();
118
+ if (name) {
119
+ const targetProp = targetPropertyMap.get(name);
120
+
121
+ if (targetProp) {
122
+ const targetInitializer = getInitializer(targetProp);
123
+ const sourceInitializer = getInitializer(sourceProp);
124
+
125
+ if (
126
+ targetInitializer &&
127
+ sourceInitializer &&
128
+ ts.isObjectLiteralExpression(targetInitializer.compilerNode) &&
129
+ ts.isObjectLiteralExpression(sourceInitializer.compilerNode)
130
+ ) {
131
+ mergeObjectLiterals(targetInitializer, sourceInitializer);
132
+ } else if (
133
+ targetInitializer &&
134
+ sourceInitializer &&
135
+ ts.isArrayLiteralExpression(targetInitializer.compilerNode) &&
136
+ ts.isArrayLiteralExpression(sourceInitializer.compilerNode)
137
+ ) {
138
+ mergeArrays(targetInitializer, sourceInitializer);
139
+ } else {
140
+ targetProp.replaceWithText(sourceProp.getFullText());
141
+ }
142
+ } else {
143
+ target.addProperty(sourceProp.getFullText());
144
+ }
145
+ } else {
146
+ target.addProperty(sourceProp.getFullText());
147
+ }
148
+ });
149
+ }
150
+
151
+ function mergeArrays(targetArray, sourceArray) {
152
+ const targetElements = targetArray.getElements();
153
+ const sourceElements = sourceArray.getElements();
154
+
155
+ const targetTexts = targetElements.map((el) => el.getText());
156
+ const sourceTexts = sourceElements.map((el) => el.getText());
157
+
158
+ const mergedTexts = [...targetTexts, ...sourceTexts];
159
+
160
+ const mergedArrayText = `[${mergedTexts.join(", ")}]`;
161
+ targetArray.replaceWithText(mergedArrayText);
162
+ }
163
+
164
+ function getInitializer(prop) {
165
+ if (prop.isKind(ts.SyntaxKind.PropertyAssignment)) {
166
+ return prop.getInitializer();
167
+ } else if (prop.isKind(ts.SyntaxKind.ShorthandPropertyAssignment)) {
168
+ return prop.getNameNode();
169
+ } else if (prop.isKind(ts.SyntaxKind.MethodDeclaration)) {
170
+ return prop;
171
+ }
172
+ return null;
173
+ }
174
+
175
+ function groupAndSortImports(importDeclarations) {
176
+ const externalImports = [];
177
+ const builtInImports = [];
178
+ const relativeImports = [];
179
+
180
+ const importMap = new Map();
181
+
182
+ importDeclarations.forEach((importDecl) => {
183
+ const moduleSpecifier = importDecl.getModuleSpecifierValue();
184
+ const importKey = moduleSpecifier;
185
+
186
+ if (importMap.has(importKey)) {
187
+ const existingImport = importMap.get(importKey);
188
+ mergeImportDeclarations(existingImport, importDecl);
189
+ } else {
190
+ importMap.set(importKey, importDecl);
191
+
192
+ if (isRelativeImport(moduleSpecifier)) {
193
+ relativeImports.push(importDecl);
194
+ } else if (isBuiltInImport(moduleSpecifier)) {
195
+ builtInImports.push(importDecl);
196
+ } else {
197
+ externalImports.push(importDecl);
198
+ }
199
+ }
200
+ });
201
+
202
+ builtInImports.sort(compareModuleSpecifiers);
203
+ externalImports.sort(compareModuleSpecifiers);
204
+ relativeImports.sort(compareModuleSpecifiers);
205
+
206
+ return [...builtInImports, ...externalImports, ...relativeImports];
207
+ }
208
+
209
+ function isRelativeImport(moduleSpecifier) {
210
+ return (
211
+ moduleSpecifier.startsWith("./") ||
212
+ moduleSpecifier.startsWith("../") ||
213
+ moduleSpecifier === "." ||
214
+ moduleSpecifier === ".."
215
+ );
216
+ }
217
+
218
+ function isBuiltInImport(moduleSpecifier) {
219
+ return moduleSpecifier.startsWith("node:");
220
+ }
221
+
222
+ function compareModuleSpecifiers(a, b) {
223
+ const specA = a.getModuleSpecifierValue();
224
+ const specB = b.getModuleSpecifierValue();
225
+ return specA.localeCompare(specB);
226
+ }
227
+
228
+ function mergeImportDeclarations(targetImport, sourceImport) {
229
+ const targetNamedImports = new Set(
230
+ targetImport.getNamedImports().map((ni) => ni.getText())
231
+ );
232
+ sourceImport.getNamedImports().forEach((ni) => {
233
+ const name = ni.getText();
234
+ if (!targetNamedImports.has(name)) {
235
+ targetImport.addNamedImport(name);
236
+ targetNamedImports.add(name);
237
+ }
238
+ });
239
+
240
+ const targetDefaultImport = targetImport.getDefaultImport()?.getText();
241
+ const sourceDefaultImport = sourceImport.getDefaultImport()?.getText();
242
+
243
+ if (!targetDefaultImport && sourceDefaultImport) {
244
+ targetImport.setDefaultImport(sourceDefaultImport);
245
+ }
246
+ }
@@ -0,0 +1,259 @@
1
+ import {
2
+ createPrompt,
3
+ isDownKey,
4
+ isEnterKey,
5
+ isNumberKey,
6
+ isSpaceKey,
7
+ isUpKey,
8
+ makeTheme,
9
+ Separator,
10
+ useKeypress,
11
+ useMemo,
12
+ usePagination,
13
+ usePrefix,
14
+ useRef,
15
+ useState,
16
+ ValidationError,
17
+ } from "@inquirer/core";
18
+ import figures from "@inquirer/figures";
19
+ import ansiEscapes from "ansi-escapes";
20
+ import colors from "picocolors";
21
+
22
+ const checkboxTheme = {
23
+ icon: {
24
+ checked: colors.green(figures.circleFilled),
25
+ unchecked: figures.circle,
26
+ cursor: figures.pointer,
27
+ },
28
+ style: {
29
+ disabledChoice: (text) => colors.dim(`- ${text}`),
30
+ renderSelectedChoices: (selectedChoices) =>
31
+ selectedChoices.map((choice) => choice.short).join(", "),
32
+ description: (text) => colors.cyan(text),
33
+ },
34
+ helpMode: "auto",
35
+ };
36
+
37
+ function isSelectable(item) {
38
+ return !Separator.isSeparator(item) && !item.disabled;
39
+ }
40
+
41
+ function isChecked(item) {
42
+ return isSelectable(item) && Boolean(item.checked);
43
+ }
44
+
45
+ function toggle(item) {
46
+ return isSelectable(item) ? { ...item, checked: !item.checked } : item;
47
+ }
48
+
49
+ function check(checked) {
50
+ return function (item) {
51
+ return isSelectable(item) ? { ...item, checked } : item;
52
+ };
53
+ }
54
+
55
+ function normalizeChoices(choices) {
56
+ return choices.map((choice) => {
57
+ if (Separator.isSeparator(choice)) return choice;
58
+
59
+ if (typeof choice === "string") {
60
+ return {
61
+ value: choice,
62
+ name: choice,
63
+ short: choice,
64
+ disabled: false,
65
+ checked: false,
66
+ };
67
+ }
68
+
69
+ const name = choice.name ?? String(choice.value);
70
+ return {
71
+ value: choice.value,
72
+ name,
73
+ short: choice.short ?? name,
74
+ description: choice.description,
75
+ disabled: choice.disabled ?? false,
76
+ checked: choice.checked ?? false,
77
+ };
78
+ });
79
+ }
80
+
81
+ export default createPrompt((config, done) => {
82
+ const {
83
+ instructions,
84
+ pageSize = 7,
85
+ loop = true,
86
+ required,
87
+ validate = () => true,
88
+ } = config;
89
+ const theme = makeTheme(checkboxTheme, config.theme);
90
+ const firstRender = useRef(true);
91
+ const [status, setStatus] = useState("idle");
92
+ const prefix = usePrefix({ status, theme });
93
+ const [items, setItems] = useState(
94
+ normalizeChoices(
95
+ typeof config.choices === "function" ? config.choices([]) : config.choices
96
+ )
97
+ );
98
+
99
+ const bounds = useMemo(() => {
100
+ const first = items.findIndex(isSelectable);
101
+ const last = items.findLastIndex(isSelectable);
102
+
103
+ if (first === -1) {
104
+ throw new ValidationError(
105
+ "[checkbox prompt] No selectable choices. All choices are disabled."
106
+ );
107
+ }
108
+
109
+ return { first, last };
110
+ }, [items]);
111
+
112
+ const [active, setActive] = useState(bounds.first);
113
+ const [showHelpTip, setShowHelpTip] = useState(true);
114
+ const [errorMsg, setError] = useState();
115
+
116
+ useKeypress(async (key) => {
117
+ if (isEnterKey(key)) {
118
+ const selection = items.filter(isChecked);
119
+ const isValid = await validate([...selection]);
120
+ if (required && !items.some(isChecked)) {
121
+ setError("At least one choice must be selected");
122
+ } else if (isValid === true) {
123
+ setStatus("done");
124
+ done(selection.map((choice) => choice.value));
125
+ } else {
126
+ setError(isValid || "You must select a valid value");
127
+ }
128
+ } else if (isUpKey(key) || isDownKey(key)) {
129
+ if (
130
+ loop ||
131
+ (isUpKey(key) && active !== bounds.first) ||
132
+ (isDownKey(key) && active !== bounds.last)
133
+ ) {
134
+ const offset = isUpKey(key) ? -1 : 1;
135
+ let next = active;
136
+ do {
137
+ next = (next + offset + items.length) % items.length;
138
+ } while (!isSelectable(items[next]));
139
+ setActive(next);
140
+ }
141
+ } else if (isSpaceKey(key)) {
142
+ setError(undefined);
143
+ setShowHelpTip(false);
144
+ let newItems = items.map((choice, i) =>
145
+ i === active ? toggle(choice) : choice
146
+ );
147
+ if (typeof config.choices === "function") {
148
+ const selected = newItems
149
+ .filter(isChecked)
150
+ .map((choice) => choice.value);
151
+ const choices = config.choices(selected);
152
+ newItems = newItems.map((choice) => {
153
+ const updated = choices.find((c) => c.value === choice.value);
154
+ return { ...choice, ...updated };
155
+ });
156
+ }
157
+ setItems(newItems);
158
+ } else if (key.name === "a") {
159
+ const selectAll = items.some(
160
+ (choice) => isSelectable(choice) && !choice.checked
161
+ );
162
+ setItems(items.map(check(selectAll)));
163
+ } else if (key.name === "i") {
164
+ setItems(items.map(toggle));
165
+ } else if (isNumberKey(key)) {
166
+ // Adjust index to start at 1
167
+ const position = Number(key.name) - 1;
168
+ const item = items[position];
169
+ if (item != null && isSelectable(item)) {
170
+ setActive(position);
171
+ setItems(
172
+ items.map((choice, i) => (i === position ? toggle(choice) : choice))
173
+ );
174
+ }
175
+ }
176
+ });
177
+
178
+ const message = theme.style.message(config.message, status);
179
+
180
+ let description;
181
+ const page = usePagination({
182
+ items,
183
+ active,
184
+ renderItem({ item, isActive }) {
185
+ if (Separator.isSeparator(item)) {
186
+ return ` ${item.separator}`;
187
+ }
188
+
189
+ if (item.disabled) {
190
+ const disabledLabel =
191
+ typeof item.disabled === "string" ? item.disabled : "(disabled)";
192
+ return theme.style.disabledChoice(`${item.name} ${disabledLabel}`);
193
+ }
194
+
195
+ if (isActive) {
196
+ description = item.description;
197
+ }
198
+
199
+ const checkbox = item.checked ? theme.icon.checked : theme.icon.unchecked;
200
+ const color = isActive ? theme.style.highlight : (x) => x;
201
+ const cursor = isActive ? theme.icon.cursor : " ";
202
+ return color(`${cursor}${checkbox} ${item.name}`);
203
+ },
204
+ pageSize,
205
+ loop,
206
+ });
207
+
208
+ if (status === "done") {
209
+ const selection = items.filter(isChecked);
210
+ const answer = theme.style.answer(
211
+ theme.style.renderSelectedChoices(selection, items)
212
+ );
213
+
214
+ return `${prefix} ${message} ${answer}`;
215
+ }
216
+
217
+ let helpTipTop = "";
218
+ let helpTipBottom = "";
219
+ if (
220
+ theme.helpMode === "always" ||
221
+ (theme.helpMode === "auto" &&
222
+ showHelpTip &&
223
+ (instructions === undefined || instructions))
224
+ ) {
225
+ if (typeof instructions === "string") {
226
+ helpTipTop = instructions;
227
+ } else {
228
+ const keys = [
229
+ `${theme.style.key("space")} to select`,
230
+ `${theme.style.key("a")} to toggle all`,
231
+ `${theme.style.key("i")} to invert selection`,
232
+ `and ${theme.style.key("enter")} to proceed`,
233
+ ];
234
+ helpTipTop = ` (Press ${keys.join(", ")})`;
235
+ }
236
+
237
+ if (
238
+ items.length > pageSize &&
239
+ (theme.helpMode === "always" ||
240
+ (theme.helpMode === "auto" && firstRender.current))
241
+ ) {
242
+ helpTipBottom = `\n${theme.style.help("(Use arrow keys to reveal more choices)")}`;
243
+ firstRender.current = false;
244
+ }
245
+ }
246
+
247
+ const choiceDescription = description
248
+ ? `\n${theme.style.description(description)}`
249
+ : ``;
250
+
251
+ let error = "";
252
+ if (errorMsg) {
253
+ error = `\n${theme.style.error(errorMsg)}`;
254
+ }
255
+
256
+ return `${prefix} ${message}${helpTipTop}\n${page}${helpTipBottom}${choiceDescription}${error}${ansiEscapes.cursorHide}`;
257
+ });
258
+
259
+ export { Separator } from "@inquirer/core";
package/lib/files.mjs ADDED
@@ -0,0 +1,6 @@
1
+ import { readFile } from "node:fs/promises";
2
+ import { join } from "node:path";
3
+
4
+ export async function json(...path) {
5
+ return JSON.parse(await readFile(join(...path), "utf8"));
6
+ }
@@ -0,0 +1,16 @@
1
+ import prettier from "prettier";
2
+
3
+ export async function format(code, parser) {
4
+ return prettier.format(code, {
5
+ parser,
6
+ printWidth: 80,
7
+ tabWidth: 2,
8
+ useTabs: false,
9
+ semi: true,
10
+ singleQuote: false,
11
+ quoteProps: "as-needed",
12
+ trailingComma: "es5",
13
+ bracketSpacing: true,
14
+ bracketSameLine: false,
15
+ });
16
+ }