@nrwl/remix 13.7.1 → 14.7.0

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 (80) hide show
  1. package/README.md +2 -2
  2. package/generators.json +25 -0
  3. package/migrations.json +54 -0
  4. package/package.json +8 -4
  5. package/src/generators/action/action.impl.d.ts +3 -0
  6. package/src/generators/action/action.impl.js +48 -0
  7. package/src/generators/action/action.impl.js.map +1 -0
  8. package/src/generators/action/schema.d.ts +4 -0
  9. package/src/generators/action/schema.json +27 -0
  10. package/src/generators/application/application.impl.js +13 -11
  11. package/src/generators/application/application.impl.js.map +1 -1
  12. package/src/generators/application/files/.gitignore__tmpl__ +4 -0
  13. package/src/generators/application/files/app/entry.client.tsx__tmpl__ +2 -2
  14. package/src/generators/application/files/app/entry.server.tsx__tmpl__ +4 -4
  15. package/src/generators/application/files/app/root.tsx__tmpl__ +10 -156
  16. package/src/generators/application/files/app/routes/index.tsx__tmpl__ +28 -96
  17. package/src/generators/application/files/package.json__tmpl__ +4 -5
  18. package/src/generators/application/files/remix.config.js__tmpl__ +8 -7
  19. package/src/generators/application/files/tsconfig.json__tmpl__ +5 -4
  20. package/src/generators/cypress/cypress.impl.js +5 -5
  21. package/src/generators/cypress/cypress.impl.js.map +1 -1
  22. package/src/generators/library/library.impl.js +1 -1
  23. package/src/generators/loader/loader.impl.d.ts +3 -0
  24. package/src/generators/loader/loader.impl.js +48 -0
  25. package/src/generators/loader/loader.impl.js.map +1 -0
  26. package/src/generators/loader/schema.d.ts +4 -0
  27. package/src/generators/loader/schema.json +27 -0
  28. package/src/generators/meta/meta.impl.d.ts +3 -0
  29. package/src/generators/meta/meta.impl.js +29 -0
  30. package/src/generators/meta/meta.impl.js.map +1 -0
  31. package/src/generators/meta/schema.d.ts +4 -0
  32. package/src/generators/meta/schema.json +27 -0
  33. package/src/generators/preset/preset.impl.js +1 -1
  34. package/src/generators/resource-route/resource-route.impl.d.ts +3 -0
  35. package/src/generators/resource-route/resource-route.impl.js +32 -0
  36. package/src/generators/resource-route/resource-route.impl.js.map +1 -0
  37. package/src/generators/resource-route/schema.d.ts +6 -0
  38. package/src/generators/resource-route/schema.json +44 -0
  39. package/src/generators/route/route.impl.js +38 -62
  40. package/src/generators/route/route.impl.js.map +1 -1
  41. package/src/generators/route/schema.d.ts +3 -0
  42. package/src/generators/route/schema.json +24 -9
  43. package/src/generators/setup/setup.impl.js +1 -1
  44. package/src/generators/style/schema.d.ts +4 -0
  45. package/src/generators/style/schema.json +34 -0
  46. package/src/generators/style/style.impl.d.ts +3 -0
  47. package/src/generators/style/style.impl.js +41 -0
  48. package/src/generators/style/style.impl.js.map +1 -0
  49. package/src/migrations/update-14-5-4/update-tsconfig-and-remix-config-for-1-6-8.d.ts +6 -0
  50. package/src/migrations/update-14-5-4/update-tsconfig-and-remix-config-for-1-6-8.js +45 -0
  51. package/src/migrations/update-14-5-4/update-tsconfig-and-remix-config-for-1-6-8.js.map +1 -0
  52. package/src/utils/get-default-export-name.d.ts +2 -0
  53. package/src/utils/get-default-export-name.js +10 -0
  54. package/src/utils/get-default-export-name.js.map +1 -0
  55. package/src/utils/get-default-export.d.ts +2 -0
  56. package/src/utils/get-default-export.js +16 -0
  57. package/src/utils/get-default-export.js.map +1 -0
  58. package/src/utils/insert-import.d.ts +4 -0
  59. package/src/utils/insert-import.js +55 -0
  60. package/src/utils/insert-import.js.map +1 -0
  61. package/src/utils/insert-statement-after-imports.d.ts +5 -0
  62. package/src/utils/insert-statement-after-imports.js +29 -0
  63. package/src/utils/insert-statement-after-imports.js.map +1 -0
  64. package/src/utils/insert-statement-in-default-function.d.ts +2 -0
  65. package/src/utils/insert-statement-in-default-function.js +24 -0
  66. package/src/utils/insert-statement-in-default-function.js.map +1 -0
  67. package/src/utils/remix-route-utils.d.ts +11 -0
  68. package/src/utils/remix-route-utils.js +53 -0
  69. package/src/utils/remix-route-utils.js.map +1 -0
  70. package/src/generators/application/files/app/routes/demos/about/index.tsx__tmpl__ +0 -17
  71. package/src/generators/application/files/app/routes/demos/about/whoa.tsx__tmpl__ +0 -20
  72. package/src/generators/application/files/app/routes/demos/about.tsx__tmpl__ +0 -44
  73. package/src/generators/application/files/app/routes/demos/actions.tsx__tmpl__ +0 -101
  74. package/src/generators/application/files/app/routes/demos/correct.tsx__tmpl__ +0 -3
  75. package/src/generators/application/files/app/routes/demos/params/$id.tsx__tmpl__ +0 -110
  76. package/src/generators/application/files/app/routes/demos/params/index.tsx__tmpl__ +0 -40
  77. package/src/generators/application/files/app/routes/demos/params.tsx__tmpl__ +0 -43
  78. package/src/generators/application/files/app/styles/dark.css +0 -7
  79. package/src/generators/application/files/app/styles/demos/about.css +0 -26
  80. package/src/generators/application/files/app/styles/global.css +0 -216
@@ -0,0 +1,41 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const tslib_1 = require("tslib");
4
+ const devkit_1 = require("@nrwl/devkit");
5
+ const insert_import_1 = require("../../utils/insert-import");
6
+ const insert_statement_after_imports_1 = require("../../utils/insert-statement-after-imports");
7
+ const remix_route_utils_1 = require("../../utils/remix-route-utils");
8
+ function default_1(tree, options) {
9
+ return tslib_1.__awaiter(this, void 0, void 0, function* () {
10
+ const { fileName: routePath, className: componentName } = (0, devkit_1.names)(options.path.replace(/^\//, '').replace(/\/$/, '').replace('.tsx', ''));
11
+ const project = (0, devkit_1.readProjectConfiguration)(tree, options.project);
12
+ if (!project)
13
+ throw new Error(`Project does not exist: ${options.project}`);
14
+ const normalizedRoutePath = (0, remix_route_utils_1.normalizeRoutePath)(routePath, project.root);
15
+ const stylesheetPath = (0, devkit_1.joinPathFragments)(project.root, 'app/styles', `${normalizedRoutePath}.css`);
16
+ tree.write(stylesheetPath, (0, devkit_1.stripIndents) `
17
+ :root {
18
+ --color-foreground: #fff;
19
+ --color-background: #143157;
20
+ --color-links: hsl(214, 73%, 69%);
21
+ --color-border: #275da8;
22
+ --font-body: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas,
23
+ Liberation Mono, Courier New, monospace;
24
+ }
25
+ `);
26
+ const routeFilePath = (0, remix_route_utils_1.resolveRemixRouteFile)(tree, options.path, options.project, '.tsx');
27
+ (0, insert_import_1.insertImport)(tree, routeFilePath, 'LinksFunction', '@remix-run/node', {
28
+ typeOnly: true,
29
+ });
30
+ (0, insert_statement_after_imports_1.insertStatementAfterImports)(tree, routeFilePath, `
31
+ import stylesUrl from '~/styles/${normalizedRoutePath}.css'
32
+
33
+ export const links: LinksFunction = () => {
34
+ return [{ rel: 'stylesheet', href: stylesUrl }];
35
+ };
36
+ `);
37
+ yield (0, devkit_1.formatFiles)(tree);
38
+ });
39
+ }
40
+ exports.default = default_1;
41
+ //# sourceMappingURL=style.impl.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"style.impl.js","sourceRoot":"","sources":["../../../../../../packages/remix/src/generators/style/style.impl.ts"],"names":[],"mappings":";;;AAAA,yCAOsB;AAGtB,6DAAyD;AACzD,+FAAyF;AACzF,qEAGuC;AAEvC,mBAA+B,IAAU,EAAE,OAAyB;;QAClE,MAAM,EAAE,QAAQ,EAAE,SAAS,EAAE,SAAS,EAAE,aAAa,EAAE,GAAG,IAAA,cAAK,EAC7D,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CACvE,CAAC;QAEF,MAAM,OAAO,GAAG,IAAA,iCAAwB,EAAC,IAAI,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC;QAChE,IAAI,CAAC,OAAO;YAAE,MAAM,IAAI,KAAK,CAAC,2BAA2B,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC;QAE5E,MAAM,mBAAmB,GAAG,IAAA,sCAAkB,EAAC,SAAS,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;QAExE,MAAM,cAAc,GAAG,IAAA,0BAAiB,EACtC,OAAO,CAAC,IAAI,EACZ,YAAY,EACZ,GAAG,mBAAmB,MAAM,CAC7B,CAAC;QAEF,IAAI,CAAC,KAAK,CACR,cAAc,EACd,IAAA,qBAAY,EAAA;;;;;;;;;KASX,CACF,CAAC;QAEF,MAAM,aAAa,GAAG,IAAA,yCAAqB,EACzC,IAAI,EACJ,OAAO,CAAC,IAAI,EACZ,OAAO,CAAC,OAAO,EACf,MAAM,CACP,CAAC;QAEF,IAAA,4BAAY,EAAC,IAAI,EAAE,aAAa,EAAE,eAAe,EAAE,iBAAiB,EAAE;YACpE,QAAQ,EAAE,IAAI;SACf,CAAC,CAAC;QAEH,IAAA,4DAA2B,EACzB,IAAI,EACJ,aAAa,EACb;sCACkC,mBAAmB;;;;;GAKtD,CACA,CAAC;QAEF,MAAM,IAAA,oBAAW,EAAC,IAAI,CAAC,CAAC;IAC1B,CAAC;CAAA;AAtDD,4BAsDC"}
@@ -0,0 +1,6 @@
1
+ import { Tree } from '@nrwl/devkit';
2
+ /**
3
+ * Update tsconfig.json and remix.config.js to support importing workspaces libraries
4
+ * @param tree
5
+ */
6
+ export default function update(tree: Tree): Promise<void>;
@@ -0,0 +1,45 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const tslib_1 = require("tslib");
4
+ const devkit_1 = require("@nrwl/devkit");
5
+ /**
6
+ * Update tsconfig.json and remix.config.js to support importing workspaces libraries
7
+ * @param tree
8
+ */
9
+ function update(tree) {
10
+ return tslib_1.__awaiter(this, void 0, void 0, function* () {
11
+ const projects = (0, devkit_1.getProjects)(tree);
12
+ const remixProjects = Array.from(projects.values()).filter((project) => {
13
+ const remixConfigPath = (0, devkit_1.joinPathFragments)(project.root, 'remix.config.js');
14
+ // if the project doesn't have a remix.config.js, it's not a Remix project so we can skip it
15
+ return tree.exists(remixConfigPath);
16
+ });
17
+ if (remixProjects.length === 0)
18
+ return;
19
+ remixProjects.forEach((project) => {
20
+ const remixConfigPath = (0, devkit_1.joinPathFragments)(project.root, 'remix.config.js');
21
+ try {
22
+ const remixConfigContent = tree.read(remixConfigPath, 'utf-8');
23
+ // if watchPaths is already there, we assume this project has been updated before
24
+ if (remixConfigContent.includes('watchPaths'))
25
+ return;
26
+ const projectOffsetFromRoot = (0, devkit_1.offsetFromRoot)(project.root);
27
+ const layout = (0, devkit_1.getWorkspaceLayout)(tree);
28
+ tree.write(remixConfigPath, remixConfigContent.replace('module.exports = {\n', `module.exports = { watchPaths: ['${(0, devkit_1.joinPathFragments)(projectOffsetFromRoot, layout.libsDir)}'],\n`));
29
+ const tsConfigPath = (0, devkit_1.joinPathFragments)(project.root, 'tsconfig.json');
30
+ const tsConfigContent = (0, devkit_1.readJson)(tree, tsConfigPath);
31
+ const newTsConfigContent = Object.assign(Object.assign({}, tsConfigContent), { extends: `${projectOffsetFromRoot}tsconfig.base.json` });
32
+ delete newTsConfigContent.compilerOptions.baseUrl;
33
+ devkit_1.logger.info(`Remix apps now support importing from non-buildable libs. However, you must remove the \`paths\` configuration from the project's \`tsconfig.json\`.\n\nMigrate any import paths using \`~\` to relative path imports and then delete the \`paths\` property from \`${tsConfigPath}\``);
34
+ (0, devkit_1.writeJson)(tree, tsConfigPath, newTsConfigContent);
35
+ }
36
+ catch (err) {
37
+ devkit_1.logger.error(err);
38
+ devkit_1.logger.error(`Unable to update ${remixConfigPath} for project ${project.root}.`);
39
+ }
40
+ });
41
+ yield (0, devkit_1.formatFiles)(tree);
42
+ });
43
+ }
44
+ exports.default = update;
45
+ //# sourceMappingURL=update-tsconfig-and-remix-config-for-1-6-8.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"update-tsconfig-and-remix-config-for-1-6-8.js","sourceRoot":"","sources":["../../../../../../packages/remix/src/migrations/update-14-5-4/update-tsconfig-and-remix-config-for-1-6-8.ts"],"names":[],"mappings":";;;AAAA,yCAUsB;AAEtB;;;GAGG;AACH,SAA8B,MAAM,CAAC,IAAU;;QAC7C,MAAM,QAAQ,GAAG,IAAA,oBAAW,EAAC,IAAI,CAAC,CAAC;QAEnC,MAAM,aAAa,GAAG,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,OAAO,EAAE,EAAE;YACrE,MAAM,eAAe,GAAG,IAAA,0BAAiB,EAAC,OAAO,CAAC,IAAI,EAAE,iBAAiB,CAAC,CAAC;YAC3E,4FAA4F;YAC5F,OAAO,IAAI,CAAC,MAAM,CAAC,eAAe,CAAC,CAAC;QACtC,CAAC,CAAC,CAAC;QAEH,IAAI,aAAa,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO;QAEvC,aAAa,CAAC,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;YAChC,MAAM,eAAe,GAAG,IAAA,0BAAiB,EAAC,OAAO,CAAC,IAAI,EAAE,iBAAiB,CAAC,CAAC;YAE3E,IAAI;gBACF,MAAM,kBAAkB,GAAG,IAAI,CAAC,IAAI,CAAC,eAAe,EAAE,OAAO,CAAC,CAAC;gBAC/D,iFAAiF;gBACjF,IAAI,kBAAkB,CAAC,QAAQ,CAAC,YAAY,CAAC;oBAAE,OAAO;gBAEtD,MAAM,qBAAqB,GAAG,IAAA,uBAAc,EAAC,OAAO,CAAC,IAAI,CAAC,CAAC;gBAC3D,MAAM,MAAM,GAAG,IAAA,2BAAkB,EAAC,IAAI,CAAC,CAAC;gBACxC,IAAI,CAAC,KAAK,CACR,eAAe,EACf,kBAAkB,CAAC,OAAO,CACxB,sBAAsB,EACtB,qCAAqC,IAAA,0BAAiB,EACpD,qBAAqB,EACrB,MAAM,CAAC,OAAO,CACf,OAAO,CACT,CACF,CAAC;gBAEF,MAAM,YAAY,GAAG,IAAA,0BAAiB,EAAC,OAAO,CAAC,IAAI,EAAE,eAAe,CAAC,CAAC;gBACtE,MAAM,eAAe,GAAG,IAAA,iBAAQ,EAAC,IAAI,EAAE,YAAY,CAAC,CAAC;gBAErD,MAAM,kBAAkB,mCACnB,eAAe,KAClB,OAAO,EAAE,GAAG,qBAAqB,oBAAoB,GACtD,CAAC;gBAEF,OAAO,kBAAkB,CAAC,eAAe,CAAC,OAAO,CAAC;gBAElD,eAAM,CAAC,IAAI,CACT,uQAAuQ,YAAY,IAAI,CACxR,CAAC;gBAEF,IAAA,kBAAS,EAAC,IAAI,EAAE,YAAY,EAAE,kBAAkB,CAAC,CAAC;aACnD;YAAC,OAAO,GAAG,EAAE;gBACZ,eAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;gBAClB,eAAM,CAAC,KAAK,CACV,oBAAoB,eAAe,gBAAgB,OAAO,CAAC,IAAI,GAAG,CACnE,CAAC;aACH;QACH,CAAC,CAAC,CAAC;QACH,MAAM,IAAA,oBAAW,EAAC,IAAI,CAAC,CAAC;IAC1B,CAAC;CAAA;AAvDD,yBAuDC"}
@@ -0,0 +1,2 @@
1
+ import { Tree } from '@nrwl/devkit';
2
+ export declare function getDefaultExportName(tree: Tree, path: string): string;
@@ -0,0 +1,10 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getDefaultExportName = void 0;
4
+ const get_default_export_1 = require("./get-default-export");
5
+ function getDefaultExportName(tree, path) {
6
+ var _a, _b;
7
+ return (_b = (_a = (0, get_default_export_1.getDefaultExport)(tree, path)) === null || _a === void 0 ? void 0 : _a.name.text) !== null && _b !== void 0 ? _b : 'Unknown';
8
+ }
9
+ exports.getDefaultExportName = getDefaultExportName;
10
+ //# sourceMappingURL=get-default-export-name.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"get-default-export-name.js","sourceRoot":"","sources":["../../../../../packages/remix/src/utils/get-default-export-name.ts"],"names":[],"mappings":";;;AACA,6DAAwD;AAExD,SAAgB,oBAAoB,CAAC,IAAU,EAAE,IAAY;;IAC3D,OAAO,MAAA,MAAA,IAAA,qCAAgB,EAAC,IAAI,EAAE,IAAI,CAAC,0CAAE,IAAI,CAAC,IAAI,mCAAI,SAAS,CAAC;AAC9D,CAAC;AAFD,oDAEC"}
@@ -0,0 +1,2 @@
1
+ import { Tree } from '@nrwl/devkit';
2
+ export declare function getDefaultExport(tree: Tree, path: string): import("typescript").FunctionDeclaration;
@@ -0,0 +1,16 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getDefaultExport = void 0;
4
+ const typescript_1 = require("typescript");
5
+ function getDefaultExport(tree, path) {
6
+ const contents = tree.read(path, 'utf-8');
7
+ const sourceFile = (0, typescript_1.createSourceFile)(path, contents, typescript_1.ScriptTarget.ESNext);
8
+ const functionDeclarations = sourceFile.statements.filter(typescript_1.isFunctionDeclaration);
9
+ return functionDeclarations.find((functionDeclaration) => {
10
+ const isDefault = functionDeclaration.modifiers.find((mod) => mod.kind === typescript_1.SyntaxKind.DefaultKeyword);
11
+ const isExport = functionDeclaration.modifiers.find((mod) => mod.kind === typescript_1.SyntaxKind.ExportKeyword);
12
+ return isDefault && isExport;
13
+ });
14
+ }
15
+ exports.getDefaultExport = getDefaultExport;
16
+ //# sourceMappingURL=get-default-export.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"get-default-export.js","sourceRoot":"","sources":["../../../../../packages/remix/src/utils/get-default-export.ts"],"names":[],"mappings":";;;AACA,2CAKoB;AAEpB,SAAgB,gBAAgB,CAAC,IAAU,EAAE,IAAY;IACvD,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IAE1C,MAAM,UAAU,GAAG,IAAA,6BAAgB,EAAC,IAAI,EAAE,QAAQ,EAAE,yBAAY,CAAC,MAAM,CAAC,CAAC;IAEzE,MAAM,oBAAoB,GAAG,UAAU,CAAC,UAAU,CAAC,MAAM,CACvD,kCAAqB,CACtB,CAAC;IAEF,OAAO,oBAAoB,CAAC,IAAI,CAAC,CAAC,mBAAmB,EAAE,EAAE;QACvD,MAAM,SAAS,GAAG,mBAAmB,CAAC,SAAS,CAAC,IAAI,CAClD,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,IAAI,KAAK,uBAAU,CAAC,cAAc,CAChD,CAAC;QAEF,MAAM,QAAQ,GAAG,mBAAmB,CAAC,SAAS,CAAC,IAAI,CACjD,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,IAAI,KAAK,uBAAU,CAAC,aAAa,CAC/C,CAAC;QAEF,OAAO,SAAS,IAAI,QAAQ,CAAC;IAC/B,CAAC,CAAC,CAAC;AACL,CAAC;AApBD,4CAoBC"}
@@ -0,0 +1,4 @@
1
+ import { Tree } from '@nrwl/devkit';
2
+ export declare function insertImport(tree: Tree, path: string, name: string, modulePath: string, options?: {
3
+ typeOnly: boolean;
4
+ }): void;
@@ -0,0 +1,55 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.insertImport = void 0;
4
+ const insert_statement_after_imports_1 = require("./insert-statement-after-imports");
5
+ const devkit_1 = require("@nrwl/devkit");
6
+ const typescript_1 = require("typescript");
7
+ function insertImport(tree, path, name, modulePath, options = { typeOnly: false }) {
8
+ if (!tree.exists(path))
9
+ throw Error(`Could not insert import ${name} from ${modulePath} in ${path}: path not found`);
10
+ const contents = tree.read(path, 'utf-8');
11
+ const sourceFile = (0, typescript_1.createSourceFile)(path, contents, typescript_1.ScriptTarget.ESNext);
12
+ let importStatements = sourceFile.statements.filter(typescript_1.isImportDeclaration);
13
+ if (options.typeOnly) {
14
+ importStatements = importStatements.filter((node) => node.importClause.isTypeOnly);
15
+ }
16
+ else {
17
+ importStatements = importStatements.filter((node) => !node.importClause.isTypeOnly);
18
+ }
19
+ const existingImport = importStatements.find((statement) => (0, typescript_1.isStringLiteral)(statement.moduleSpecifier) &&
20
+ statement.moduleSpecifier
21
+ .getText(sourceFile)
22
+ .replace(/['"`]/g, '')
23
+ .trim() === modulePath &&
24
+ statement.importClause.namedBindings &&
25
+ (0, typescript_1.isNamedImports)(statement.importClause.namedBindings));
26
+ if (!existingImport) {
27
+ (0, insert_statement_after_imports_1.insertStatementAfterImports)(tree, path, options.typeOnly
28
+ ? `import type { ${name} } from '${modulePath}';`
29
+ : `import { ${name} } from '${modulePath}';`);
30
+ return;
31
+ }
32
+ const namedImports = existingImport.importClause
33
+ .namedBindings;
34
+ const alreadyImported = namedImports.elements.find((element) => element.name.escapedText === name) !== undefined;
35
+ if (!alreadyImported) {
36
+ const index = namedImports.getEnd() - 1;
37
+ let text;
38
+ if (namedImports.elements.hasTrailingComma) {
39
+ text = `${name},`;
40
+ }
41
+ else {
42
+ text = `,${name}`;
43
+ }
44
+ const newContents = (0, devkit_1.applyChangesToString)(contents, [
45
+ {
46
+ type: devkit_1.ChangeType.Insert,
47
+ index,
48
+ text,
49
+ },
50
+ ]);
51
+ tree.write(path, newContents);
52
+ }
53
+ }
54
+ exports.insertImport = insertImport;
55
+ //# sourceMappingURL=insert-import.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"insert-import.js","sourceRoot":"","sources":["../../../../../packages/remix/src/utils/insert-import.ts"],"names":[],"mappings":";;;AAAA,qFAA+E;AAC/E,yCAAsE;AACtE,2CAOoB;AAEpB,SAAgB,YAAY,CAC1B,IAAU,EACV,IAAY,EACZ,IAAY,EACZ,UAAkB,EAClB,UAAiC,EAAE,QAAQ,EAAE,KAAK,EAAE;IAEpD,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC;QACpB,MAAM,KAAK,CACT,2BAA2B,IAAI,SAAS,UAAU,OAAO,IAAI,kBAAkB,CAChF,CAAC;IAEJ,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IAE1C,MAAM,UAAU,GAAG,IAAA,6BAAgB,EAAC,IAAI,EAAE,QAAQ,EAAE,yBAAY,CAAC,MAAM,CAAC,CAAC;IAEzE,IAAI,gBAAgB,GAAG,UAAU,CAAC,UAAU,CAAC,MAAM,CAAC,gCAAmB,CAAC,CAAC;IAEzE,IAAI,OAAO,CAAC,QAAQ,EAAE;QACpB,gBAAgB,GAAG,gBAAgB,CAAC,MAAM,CACxC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,YAAY,CAAC,UAAU,CACvC,CAAC;KACH;SAAM;QACL,gBAAgB,GAAG,gBAAgB,CAAC,MAAM,CACxC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,UAAU,CACxC,CAAC;KACH;IAED,MAAM,cAAc,GAAG,gBAAgB,CAAC,IAAI,CAC1C,CAAC,SAAS,EAAE,EAAE,CACZ,IAAA,4BAAe,EAAC,SAAS,CAAC,eAAe,CAAC;QAC1C,SAAS,CAAC,eAAe;aACtB,OAAO,CAAC,UAAU,CAAC;aACnB,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC;aACrB,IAAI,EAAE,KAAK,UAAU;QACxB,SAAS,CAAC,YAAY,CAAC,aAAa;QACpC,IAAA,2BAAc,EAAC,SAAS,CAAC,YAAY,CAAC,aAAa,CAAC,CACvD,CAAC;IAEF,IAAI,CAAC,cAAc,EAAE;QACnB,IAAA,4DAA2B,EACzB,IAAI,EACJ,IAAI,EACJ,OAAO,CAAC,QAAQ;YACd,CAAC,CAAC,iBAAiB,IAAI,YAAY,UAAU,IAAI;YACjD,CAAC,CAAC,YAAY,IAAI,YAAY,UAAU,IAAI,CAC/C,CAAC;QACF,OAAO;KACR;IAED,MAAM,YAAY,GAAG,cAAc,CAAC,YAAY;SAC7C,aAA6B,CAAC;IAEjC,MAAM,eAAe,GACnB,YAAY,CAAC,QAAQ,CAAC,IAAI,CACxB,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,WAAW,KAAK,IAAI,CAC/C,KAAK,SAAS,CAAC;IAElB,IAAI,CAAC,eAAe,EAAE;QACpB,MAAM,KAAK,GAAG,YAAY,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;QAExC,IAAI,IAAY,CAAC;QACjB,IAAI,YAAY,CAAC,QAAQ,CAAC,gBAAgB,EAAE;YAC1C,IAAI,GAAG,GAAG,IAAI,GAAG,CAAC;SACnB;aAAM;YACL,IAAI,GAAG,IAAI,IAAI,EAAE,CAAC;SACnB;QAED,MAAM,WAAW,GAAG,IAAA,6BAAoB,EAAC,QAAQ,EAAE;YACjD;gBACE,IAAI,EAAE,mBAAU,CAAC,MAAM;gBACvB,KAAK;gBACL,IAAI;aACL;SACF,CAAC,CAAC;QAEH,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,WAAW,CAAC,CAAC;KAC/B;AACH,CAAC;AA9ED,oCA8EC"}
@@ -0,0 +1,5 @@
1
+ import { Tree } from '@nrwl/devkit';
2
+ /**
3
+ * Insert a statement after the last import statement in a file
4
+ */
5
+ export declare function insertStatementAfterImports(tree: Tree, path: string, statement: string): void;
@@ -0,0 +1,29 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.insertStatementAfterImports = void 0;
4
+ const devkit_1 = require("@nrwl/devkit");
5
+ const typescript_1 = require("typescript");
6
+ /**
7
+ * Insert a statement after the last import statement in a file
8
+ */
9
+ function insertStatementAfterImports(tree, path, statement) {
10
+ const contents = tree.read(path, 'utf-8');
11
+ const sourceFile = (0, typescript_1.createSourceFile)(path, contents, typescript_1.ScriptTarget.ESNext);
12
+ const importStatements = sourceFile.statements.filter(typescript_1.isImportDeclaration);
13
+ const index = importStatements.length > 0
14
+ ? importStatements[importStatements.length - 1].getEnd()
15
+ : 0;
16
+ if (importStatements.length > 0) {
17
+ statement = `\n${statement}`;
18
+ }
19
+ const newContents = (0, devkit_1.applyChangesToString)(contents, [
20
+ {
21
+ type: devkit_1.ChangeType.Insert,
22
+ index,
23
+ text: statement,
24
+ },
25
+ ]);
26
+ tree.write(path, newContents);
27
+ }
28
+ exports.insertStatementAfterImports = insertStatementAfterImports;
29
+ //# sourceMappingURL=insert-statement-after-imports.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"insert-statement-after-imports.js","sourceRoot":"","sources":["../../../../../packages/remix/src/utils/insert-statement-after-imports.ts"],"names":[],"mappings":";;;AAAA,yCAAsE;AACtE,2CAIoB;AAEpB;;GAEG;AACH,SAAgB,2BAA2B,CACzC,IAAU,EACV,IAAY,EACZ,SAAiB;IAEjB,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IAE1C,MAAM,UAAU,GAAG,IAAA,6BAAgB,EAAC,IAAI,EAAE,QAAQ,EAAE,yBAAY,CAAC,MAAM,CAAC,CAAC;IAEzE,MAAM,gBAAgB,GAAG,UAAU,CAAC,UAAU,CAAC,MAAM,CAAC,gCAAmB,CAAC,CAAC;IAC3E,MAAM,KAAK,GACT,gBAAgB,CAAC,MAAM,GAAG,CAAC;QACzB,CAAC,CAAC,gBAAgB,CAAC,gBAAgB,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,MAAM,EAAE;QACxD,CAAC,CAAC,CAAC,CAAC;IAER,IAAI,gBAAgB,CAAC,MAAM,GAAG,CAAC,EAAE;QAC/B,SAAS,GAAG,KAAK,SAAS,EAAE,CAAC;KAC9B;IAED,MAAM,WAAW,GAAG,IAAA,6BAAoB,EAAC,QAAQ,EAAE;QACjD;YACE,IAAI,EAAE,mBAAU,CAAC,MAAM;YACvB,KAAK;YACL,IAAI,EAAE,SAAS;SAChB;KACF,CAAC,CAAC;IAEH,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,WAAW,CAAC,CAAC;AAChC,CAAC;AA5BD,kEA4BC"}
@@ -0,0 +1,2 @@
1
+ import { Tree } from '@nrwl/devkit';
2
+ export declare function insertStatementInDefaultFunction(tree: Tree, path: string, statement: any): void;
@@ -0,0 +1,24 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.insertStatementInDefaultFunction = void 0;
4
+ const devkit_1 = require("@nrwl/devkit");
5
+ const get_default_export_1 = require("./get-default-export");
6
+ function insertStatementInDefaultFunction(tree, path, statement) {
7
+ const defaultExport = (0, get_default_export_1.getDefaultExport)(tree, path);
8
+ if (!defaultExport) {
9
+ throw Error('No default export found!');
10
+ }
11
+ const index = defaultExport.body.statements.length > 0
12
+ ? defaultExport.body.statements[0].pos
13
+ : 0;
14
+ const newContents = (0, devkit_1.applyChangesToString)(tree.read(path, 'utf-8'), [
15
+ {
16
+ type: devkit_1.ChangeType.Insert,
17
+ index,
18
+ text: statement,
19
+ },
20
+ ]);
21
+ tree.write(path, newContents);
22
+ }
23
+ exports.insertStatementInDefaultFunction = insertStatementInDefaultFunction;
24
+ //# sourceMappingURL=insert-statement-in-default-function.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"insert-statement-in-default-function.js","sourceRoot":"","sources":["../../../../../packages/remix/src/utils/insert-statement-in-default-function.ts"],"names":[],"mappings":";;;AAAA,yCAAsE;AACtE,6DAAwD;AAExD,SAAgB,gCAAgC,CAC9C,IAAU,EACV,IAAY,EACZ,SAAS;IAET,MAAM,aAAa,GAAG,IAAA,qCAAgB,EAAC,IAAI,EAAE,IAAI,CAAC,CAAC;IAEnD,IAAI,CAAC,aAAa,EAAE;QAClB,MAAM,KAAK,CAAC,0BAA0B,CAAC,CAAC;KACzC;IAED,MAAM,KAAK,GACT,aAAa,CAAC,IAAI,CAAC,UAAU,CAAC,MAAM,GAAG,CAAC;QACtC,CAAC,CAAC,aAAa,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,GAAG;QACtC,CAAC,CAAC,CAAC,CAAC;IAER,MAAM,WAAW,GAAG,IAAA,6BAAoB,EAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,OAAO,CAAC,EAAE;QACjE;YACE,IAAI,EAAE,mBAAU,CAAC,MAAM;YACvB,KAAK;YACL,IAAI,EAAE,SAAS;SAChB;KACF,CAAC,CAAC;IAEH,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,WAAW,CAAC,CAAC;AAChC,CAAC;AAzBD,4EAyBC"}
@@ -0,0 +1,11 @@
1
+ import { Tree } from '@nrwl/devkit';
2
+ /**
3
+ *
4
+ * @param tree
5
+ * @param path to the route which could be fully specified or just "foo/bar"
6
+ * @param projectName the name of the project where the route should be added
7
+ * @param fileExtension the file extension to add to resolved route file
8
+ * @returns file path to the route
9
+ */
10
+ export declare function resolveRemixRouteFile(tree: Tree, path: string, projectName: string, fileExtension?: string): string;
11
+ export declare function normalizeRoutePath(path: string, projectRoot: string): string;
@@ -0,0 +1,53 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.normalizeRoutePath = exports.resolveRemixRouteFile = void 0;
4
+ const devkit_1 = require("@nrwl/devkit");
5
+ /**
6
+ *
7
+ * @param tree
8
+ * @param path to the route which could be fully specified or just "foo/bar"
9
+ * @param projectName the name of the project where the route should be added
10
+ * @param fileExtension the file extension to add to resolved route file
11
+ * @returns file path to the route
12
+ */
13
+ function resolveRemixRouteFile(tree, path, projectName, fileExtension) {
14
+ const project = (0, devkit_1.readProjectConfiguration)(tree, projectName);
15
+ if (!project)
16
+ throw new Error(`Project does not exist: ${projectName}`);
17
+ const { fileName: routePath } = (0, devkit_1.names)(path.replace(/^\//, '').replace(/\/$/, ''));
18
+ const normalizedRoutePath = normalizeRoutePath(routePath, project.root);
19
+ // if no file extension specified, let's try to find it
20
+ if (!fileExtension) {
21
+ // see if the path already has it
22
+ const extensionMatch = path.match(/(\.[^.]+)$/);
23
+ if (extensionMatch) {
24
+ fileExtension = extensionMatch[0];
25
+ }
26
+ else {
27
+ // look for either .ts or .tsx to exist in tree
28
+ if (tree.exists(`${normalizedRoutePath}.ts`)) {
29
+ fileExtension = '.ts';
30
+ }
31
+ else {
32
+ // default to .tsx if nothing else found
33
+ fileExtension = '.tsx';
34
+ }
35
+ }
36
+ }
37
+ const fileName = normalizedRoutePath.endsWith(fileExtension)
38
+ ? normalizedRoutePath
39
+ : `${normalizedRoutePath}${fileExtension}`;
40
+ // TODO: what if someone changes the Remix app root folder in the remix.config.js?
41
+ const routeFilePath = (0, devkit_1.joinPathFragments)(project.root, 'app/routes', fileName);
42
+ return routeFilePath;
43
+ }
44
+ exports.resolveRemixRouteFile = resolveRemixRouteFile;
45
+ function normalizeRoutePath(path, projectRoot) {
46
+ if (path.indexOf(projectRoot) === -1)
47
+ return path;
48
+ if (path.indexOf('/routes/') > -1)
49
+ return path.substring(path.indexOf('/routes/') + 8);
50
+ return path.substring(projectRoot.length + 1);
51
+ }
52
+ exports.normalizeRoutePath = normalizeRoutePath;
53
+ //# sourceMappingURL=remix-route-utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"remix-route-utils.js","sourceRoot":"","sources":["../../../../../packages/remix/src/utils/remix-route-utils.ts"],"names":[],"mappings":";;;AAAA,yCAKsB;AAEtB;;;;;;;GAOG;AACH,SAAgB,qBAAqB,CACnC,IAAU,EACV,IAAY,EACZ,WAAmB,EACnB,aAAsB;IAEtB,MAAM,OAAO,GAAG,IAAA,iCAAwB,EAAC,IAAI,EAAE,WAAW,CAAC,CAAC;IAC5D,IAAI,CAAC,OAAO;QAAE,MAAM,IAAI,KAAK,CAAC,2BAA2B,WAAW,EAAE,CAAC,CAAC;IACxE,MAAM,EAAE,QAAQ,EAAE,SAAS,EAAE,GAAG,IAAA,cAAK,EACnC,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAC3C,CAAC;IACF,MAAM,mBAAmB,GAAG,kBAAkB,CAAC,SAAS,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;IAExE,uDAAuD;IACvD,IAAI,CAAC,aAAa,EAAE;QAClB,iCAAiC;QACjC,MAAM,cAAc,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC;QAEhD,IAAI,cAAc,EAAE;YAClB,aAAa,GAAG,cAAc,CAAC,CAAC,CAAC,CAAC;SACnC;aAAM;YACL,+CAA+C;YAC/C,IAAI,IAAI,CAAC,MAAM,CAAC,GAAG,mBAAmB,KAAK,CAAC,EAAE;gBAC5C,aAAa,GAAG,KAAK,CAAC;aACvB;iBAAM;gBACL,wCAAwC;gBACxC,aAAa,GAAG,MAAM,CAAC;aACxB;SACF;KACF;IAED,MAAM,QAAQ,GAAG,mBAAmB,CAAC,QAAQ,CAAC,aAAa,CAAC;QAC1D,CAAC,CAAC,mBAAmB;QACrB,CAAC,CAAC,GAAG,mBAAmB,GAAG,aAAa,EAAE,CAAC;IAC7C,kFAAkF;IAClF,MAAM,aAAa,GAAG,IAAA,0BAAiB,EAAC,OAAO,CAAC,IAAI,EAAE,YAAY,EAAE,QAAQ,CAAC,CAAC;IAC9E,OAAO,aAAa,CAAC;AACvB,CAAC;AArCD,sDAqCC;AAED,SAAgB,kBAAkB,CAAC,IAAY,EAAE,WAAmB;IAClE,IAAI,IAAI,CAAC,OAAO,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC;QAAE,OAAO,IAAI,CAAC;IAClD,IAAI,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC;QAC/B,OAAO,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC;IACtD,OAAO,IAAI,CAAC,SAAS,CAAC,WAAW,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;AAChD,CAAC;AALD,gDAKC"}
@@ -1,17 +0,0 @@
1
- import { Link } from "remix";
2
-
3
- export default function AboutIndex() {
4
- return (
5
- <div>
6
- <p>
7
- You are looking at the index route for the <code>/about</code> URL
8
- segment, but there are nested routes as well!
9
- </p>
10
- <p>
11
- <strong>
12
- <Link to="whoa">Check out one of them here.</Link>
13
- </strong>
14
- </p>
15
- </div>
16
- );
17
- }
@@ -1,20 +0,0 @@
1
- import { Link } from "remix";
2
-
3
- export default function AboutIndex() {
4
- return (
5
- <div>
6
- <p>
7
- Whoa, this is a nested route! We render the <code>/about</code> layout
8
- route component, and its <code>Outlet</code> renders our route
9
- component. 🤯
10
- </p>
11
- <p>
12
- <strong>
13
- <Link to="..">
14
- Go back to the <code>/about</code> index.
15
- </Link>
16
- </strong>
17
- </p>
18
- </div>
19
- );
20
- }
@@ -1,44 +0,0 @@
1
- import { Outlet } from "remix";
2
- import type { MetaFunction, LinksFunction } from "remix";
3
-
4
- import stylesUrl from "~/styles/demos/about.css";
5
-
6
- export let meta: MetaFunction = () => {
7
- return {
8
- title: "About Remix"
9
- };
10
- };
11
-
12
- export let links: LinksFunction = () => {
13
- return [{ rel: "stylesheet", href: stylesUrl }];
14
- };
15
-
16
- export default function Index() {
17
- return (
18
- <div className="about">
19
- <div className="about__intro">
20
- <h2>About Us</h2>
21
- <p>
22
- Ok, so this page isn't really <em>about us</em>, but we did want to
23
- show you a few more things Remix can do.
24
- </p>
25
- <p>
26
- Did you notice that things look a little different on this page? The
27
- CSS that we import in the route file and include in its{" "}
28
- <code>links</code> export is only included on this route and its
29
- children.
30
- </p>
31
- <p>
32
- Wait a sec...<em>its children</em>? To understand what we mean by
33
- this,{" "}
34
- <a href="https://remix.run/tutorial/4-nested-routes-params">
35
- read all about nested routes in the docs
36
- </a>
37
- .
38
- </p>
39
- <hr />
40
- <Outlet />
41
- </div>
42
- </div>
43
- );
44
- }
@@ -1,101 +0,0 @@
1
- import { useEffect, useRef } from "react";
2
- import type { ActionFunction } from "remix";
3
- import { Form, json, useActionData, redirect } from "remix";
4
-
5
- export function meta() {
6
- return { title: "Actions Demo" };
7
- }
8
-
9
- // When your form sends a POST, the action is called on the server.
10
- // - https://remix.run/api/conventions#action
11
- // - https://remix.run/guides/data-updates
12
- export let action: ActionFunction = async ({ request }) => {
13
- let formData = await request.formData();
14
- let answer = formData.get("answer");
15
-
16
- // Typical action workflows start with validating the form data that just came
17
- // over the network. Clientside validation is fine, but you definitely need it
18
- // server side. If there's a problem, return the the data and the component
19
- // can render it.
20
- if (typeof answer !== "string") {
21
- return json("Come on, at least try!", { status: 400 });
22
- }
23
-
24
- if (answer !== "egg") {
25
- return json(`Sorry, ${answer} is not right.`, { status: 400 });
26
- }
27
-
28
- // Finally, if the data is valid, you'll typically write to a database or send or
29
- // email or log the user in, etc. It's recommended to redirect after a
30
- // successful action, even if it's to the same place so that non-JavaScript workflows
31
- // from the browser doesn't repost the data if the user clicks back.
32
- return redirect("/demos/correct");
33
- };
34
-
35
- export default function ActionsDemo() {
36
- // https://remix.run/api/remix#useactiondata
37
- let actionMessage = useActionData<string>();
38
- let answerRef = useRef<HTMLInputElement>(null);
39
-
40
- // This form works without JavaScript, but when we have JavaScript we can make
41
- // the experience better by selecting the input on wrong answers! Go ahead, disable
42
- // JavaScript in your browser and see what happens.
43
- useEffect(() => {
44
- if (actionMessage && answerRef.current) {
45
- answerRef.current.select();
46
- }
47
- }, [actionMessage]);
48
-
49
- return (
50
- <div className="remix__page">
51
- <main>
52
- <h2>Actions!</h2>
53
- <p>
54
- This form submission will send a post request that we handle in our
55
- `action` export. Any route can export an action to handle data
56
- mutations.
57
- </p>
58
- <Form method="post" className="remix__form">
59
- <h3>Post an Action</h3>
60
- <p>
61
- <i>What is more useful when it is broken?</i>
62
- </p>
63
- <label>
64
- <div>Answer:</div>
65
- <input ref={answerRef} name="answer" type="text" />
66
- </label>
67
- <div>
68
- <button>Answer!</button>
69
- </div>
70
- {actionMessage ? (
71
- <p>
72
- <b>{actionMessage}</b>
73
- </p>
74
- ) : null}
75
- </Form>
76
- </main>
77
-
78
- <aside>
79
- <h3>Additional Resources</h3>
80
- <ul>
81
- <li>
82
- Guide:{" "}
83
- <a href="https://remix.run/guides/data-writes">Data Writes</a>
84
- </li>
85
- <li>
86
- API:{" "}
87
- <a href="https://remix.run/api/conventions#action">
88
- Route Action Export
89
- </a>
90
- </li>
91
- <li>
92
- API:{" "}
93
- <a href="https://remix.run/api/remix#useactiondata">
94
- <code>useActionData</code>
95
- </a>
96
- </li>
97
- </ul>
98
- </aside>
99
- </div>
100
- );
101
- }
@@ -1,3 +0,0 @@
1
- export default function NiceWork() {
2
- return <h1>You got it right!</h1>;
3
- }