@nrwl/remix 13.7.1 → 14.1.7
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/README.md +2 -2
- package/generators.json +25 -0
- package/package.json +1 -1
- package/src/generators/action/action.impl.d.ts +3 -0
- package/src/generators/action/action.impl.js +48 -0
- package/src/generators/action/action.impl.js.map +1 -0
- package/src/generators/action/schema.d.ts +4 -0
- package/src/generators/action/schema.json +27 -0
- package/src/generators/application/files/.gitignore__tmpl__ +4 -0
- package/src/generators/application/files/app/entry.client.tsx__tmpl__ +2 -2
- package/src/generators/application/files/app/entry.server.tsx__tmpl__ +4 -4
- package/src/generators/application/files/app/root.tsx__tmpl__ +10 -156
- package/src/generators/application/files/app/routes/index.tsx__tmpl__ +28 -96
- package/src/generators/application/files/package.json__tmpl__ +1 -1
- package/src/generators/application/files/remix.config.js__tmpl__ +7 -7
- package/src/generators/application/files/tsconfig.json__tmpl__ +6 -1
- package/src/generators/cypress/cypress.impl.js +3 -3
- package/src/generators/cypress/cypress.impl.js.map +1 -1
- package/src/generators/loader/loader.impl.d.ts +3 -0
- package/src/generators/loader/loader.impl.js +48 -0
- package/src/generators/loader/loader.impl.js.map +1 -0
- package/src/generators/loader/schema.d.ts +4 -0
- package/src/generators/loader/schema.json +27 -0
- package/src/generators/meta/meta.impl.d.ts +3 -0
- package/src/generators/meta/meta.impl.js +29 -0
- package/src/generators/meta/meta.impl.js.map +1 -0
- package/src/generators/meta/schema.d.ts +4 -0
- package/src/generators/meta/schema.json +27 -0
- package/src/generators/resource-route/resource-route.impl.d.ts +3 -0
- package/src/generators/resource-route/resource-route.impl.js +32 -0
- package/src/generators/resource-route/resource-route.impl.js.map +1 -0
- package/src/generators/resource-route/schema.d.ts +6 -0
- package/src/generators/resource-route/schema.json +44 -0
- package/src/generators/route/route.impl.js +37 -61
- package/src/generators/route/route.impl.js.map +1 -1
- package/src/generators/route/schema.d.ts +3 -0
- package/src/generators/route/schema.json +24 -9
- package/src/generators/style/schema.d.ts +4 -0
- package/src/generators/style/schema.json +34 -0
- package/src/generators/style/style.impl.d.ts +3 -0
- package/src/generators/style/style.impl.js +41 -0
- package/src/generators/style/style.impl.js.map +1 -0
- package/src/utils/get-default-export-name.d.ts +2 -0
- package/src/utils/get-default-export-name.js +10 -0
- package/src/utils/get-default-export-name.js.map +1 -0
- package/src/utils/get-default-export.d.ts +2 -0
- package/src/utils/get-default-export.js +16 -0
- package/src/utils/get-default-export.js.map +1 -0
- package/src/utils/insert-import.d.ts +4 -0
- package/src/utils/insert-import.js +55 -0
- package/src/utils/insert-import.js.map +1 -0
- package/src/utils/insert-statement-after-imports.d.ts +5 -0
- package/src/utils/insert-statement-after-imports.js +29 -0
- package/src/utils/insert-statement-after-imports.js.map +1 -0
- package/src/utils/insert-statement-in-default-function.d.ts +2 -0
- package/src/utils/insert-statement-in-default-function.js +24 -0
- package/src/utils/insert-statement-in-default-function.js.map +1 -0
- package/src/utils/remix-route-utils.d.ts +11 -0
- package/src/utils/remix-route-utils.js +53 -0
- package/src/utils/remix-route-utils.js.map +1 -0
- package/src/generators/application/files/app/routes/demos/about/index.tsx__tmpl__ +0 -17
- package/src/generators/application/files/app/routes/demos/about/whoa.tsx__tmpl__ +0 -20
- package/src/generators/application/files/app/routes/demos/about.tsx__tmpl__ +0 -44
- package/src/generators/application/files/app/routes/demos/actions.tsx__tmpl__ +0 -101
- package/src/generators/application/files/app/routes/demos/correct.tsx__tmpl__ +0 -3
- package/src/generators/application/files/app/routes/demos/params/$id.tsx__tmpl__ +0 -110
- package/src/generators/application/files/app/routes/demos/params/index.tsx__tmpl__ +0 -40
- package/src/generators/application/files/app/routes/demos/params.tsx__tmpl__ +0 -43
- package/src/generators/application/files/app/styles/dark.css +0 -7
- package/src/generators/application/files/app/styles/demos/about.css +0 -26
- package/src/generators/application/files/app/styles/global.css +0 -216
|
@@ -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,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,110 +0,0 @@
|
|
|
1
|
-
import { useCatch, Link, json, useLoaderData } from "remix";
|
|
2
|
-
import type { LoaderFunction, MetaFunction } from "remix";
|
|
3
|
-
|
|
4
|
-
// The `$` in route filenames becomes a pattern that's parsed from the URL and
|
|
5
|
-
// passed to your loaders so you can look up data.
|
|
6
|
-
// - https://remix.run/api/conventions#loader-params
|
|
7
|
-
export let loader: LoaderFunction = async ({ params }) => {
|
|
8
|
-
// pretend like we're using params.id to look something up in the db
|
|
9
|
-
|
|
10
|
-
if (params.id === "this-record-does-not-exist") {
|
|
11
|
-
// If the record doesn't exist we can't render the route normally, so
|
|
12
|
-
// instead we throw a 404 reponse to stop running code here and show the
|
|
13
|
-
// user the catch boundary.
|
|
14
|
-
throw new Response("Not Found", { status: 404 });
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
// now pretend like the record exists but the user just isn't authorized to
|
|
18
|
-
// see it.
|
|
19
|
-
if (params.id === "shh-its-a-secret") {
|
|
20
|
-
// Again, we can't render the component if the user isn't authorized. You
|
|
21
|
-
// can even put data in the response that might help the user rectify the
|
|
22
|
-
// issue! Like emailing the webmaster for access to the page. (Oh, right,
|
|
23
|
-
// `json` is just a Response helper that makes it easier to send JSON
|
|
24
|
-
// responses).
|
|
25
|
-
throw json({ webmasterEmail: "hello@remix.run" }, { status: 401 });
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
// Sometimes your code just blows up and you never anticipated it. Remix will
|
|
29
|
-
// automatically catch it and send the UI to the error boundary.
|
|
30
|
-
if (params.id === "kaboom") {
|
|
31
|
-
lol();
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
// but otherwise the record was found, user has access, so we can do whatever
|
|
35
|
-
// else we needed to in the loader and return the data. (This is boring, we're
|
|
36
|
-
// just gonna return the params.id).
|
|
37
|
-
return { param: params.id };
|
|
38
|
-
};
|
|
39
|
-
|
|
40
|
-
export default function ParamDemo() {
|
|
41
|
-
let data = useLoaderData();
|
|
42
|
-
return (
|
|
43
|
-
<h1>
|
|
44
|
-
The param is <i style={{ color: "red" }}>{data.param}</i>
|
|
45
|
-
</h1>
|
|
46
|
-
);
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
// https://remix.run/api/conventions#catchboundary
|
|
50
|
-
// https://remix.run/api/remix#usecatch
|
|
51
|
-
// https://remix.run/api/guides/not-found
|
|
52
|
-
export function CatchBoundary() {
|
|
53
|
-
let caught = useCatch();
|
|
54
|
-
|
|
55
|
-
let message: React.ReactNode;
|
|
56
|
-
switch (caught.status) {
|
|
57
|
-
case 401:
|
|
58
|
-
message = (
|
|
59
|
-
<p>
|
|
60
|
-
Looks like you tried to visit a page that you do not have access to.
|
|
61
|
-
Maybe ask the webmaster ({caught.data.webmasterEmail}) for access.
|
|
62
|
-
</p>
|
|
63
|
-
);
|
|
64
|
-
case 404:
|
|
65
|
-
message = (
|
|
66
|
-
<p>Looks like you tried to visit a page that does not exist.</p>
|
|
67
|
-
);
|
|
68
|
-
default:
|
|
69
|
-
message = (
|
|
70
|
-
<p>
|
|
71
|
-
There was a problem with your request!
|
|
72
|
-
<br />
|
|
73
|
-
{caught.status} {caught.statusText}
|
|
74
|
-
</p>
|
|
75
|
-
);
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
return (
|
|
79
|
-
<>
|
|
80
|
-
<h2>Oops!</h2>
|
|
81
|
-
<p>{message}</p>
|
|
82
|
-
<p>
|
|
83
|
-
(Isn't it cool that the user gets to stay in context and try a different
|
|
84
|
-
link in the parts of the UI that didn't blow up?)
|
|
85
|
-
</p>
|
|
86
|
-
</>
|
|
87
|
-
);
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
// https://remix.run/api/conventions#errorboundary
|
|
91
|
-
// https://remix.run/api/guides/not-found
|
|
92
|
-
export function ErrorBoundary({ error }: { error: Error }) {
|
|
93
|
-
console.error(error);
|
|
94
|
-
return (
|
|
95
|
-
<>
|
|
96
|
-
<h2>Error!</h2>
|
|
97
|
-
<p>{error.message}</p>
|
|
98
|
-
<p>
|
|
99
|
-
(Isn't it cool that the user gets to stay in context and try a different
|
|
100
|
-
link in the parts of the UI that didn't blow up?)
|
|
101
|
-
</p>
|
|
102
|
-
</>
|
|
103
|
-
);
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
export let meta: MetaFunction = ({ data }) => {
|
|
107
|
-
return {
|
|
108
|
-
title: data ? `Param: ${data.param}` : "Oops...",
|
|
109
|
-
};
|
|
110
|
-
};
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
import { useCatch, Link, json, useLoaderData, Outlet } from "remix";
|
|
2
|
-
import type { LoaderFunction } from "remix";
|
|
3
|
-
|
|
4
|
-
export default function Boundaries() {
|
|
5
|
-
return (
|
|
6
|
-
<>
|
|
7
|
-
<h2>Params</h2>
|
|
8
|
-
<p>
|
|
9
|
-
When you name a route segment with $ like{" "}
|
|
10
|
-
<code>routes/users/$userId.js</code>, the $ segment will be parsed from
|
|
11
|
-
the URL and sent to your loaders and actions by the same name.
|
|
12
|
-
</p>
|
|
13
|
-
<h2>Errors</h2>
|
|
14
|
-
<p>
|
|
15
|
-
When a route throws and error in it's action, loader, or component,
|
|
16
|
-
Remix automatically catches it, won't even try to render the component,
|
|
17
|
-
but it will render the route's ErrorBoundary instead. If the route
|
|
18
|
-
doesn't have one, it will bubble up to the routes above it until it hits
|
|
19
|
-
the root.
|
|
20
|
-
</p>
|
|
21
|
-
<p>So be as granular as you want with your error handling.</p>
|
|
22
|
-
<h2>Not Found</h2>
|
|
23
|
-
<p>
|
|
24
|
-
(and other{" "}
|
|
25
|
-
<a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Status#client_error_responses">
|
|
26
|
-
client errors
|
|
27
|
-
</a>
|
|
28
|
-
)
|
|
29
|
-
</p>
|
|
30
|
-
<p>
|
|
31
|
-
Loaders and Actions can throw a <code>Response</code> instead of an
|
|
32
|
-
error and Remix will render the CatchBoundary instead of the component.
|
|
33
|
-
This is great when loading data from a database isn't found. As soon as
|
|
34
|
-
you know you can't render the component normally, throw a 404 response
|
|
35
|
-
and send your app into the catch boundary. Just like error boundaries,
|
|
36
|
-
catch boundaries bubble, too.
|
|
37
|
-
</p>
|
|
38
|
-
</>
|
|
39
|
-
);
|
|
40
|
-
}
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
import { useCatch, Link, json, useLoaderData, Outlet } from "remix";
|
|
2
|
-
|
|
3
|
-
export function meta() {
|
|
4
|
-
return { title: "Boundaries Demo" };
|
|
5
|
-
}
|
|
6
|
-
|
|
7
|
-
export default function Boundaries() {
|
|
8
|
-
return (
|
|
9
|
-
<div className="remix__page">
|
|
10
|
-
<main>
|
|
11
|
-
<Outlet />
|
|
12
|
-
</main>
|
|
13
|
-
|
|
14
|
-
<aside>
|
|
15
|
-
<h2>Click these Links</h2>
|
|
16
|
-
<ul>
|
|
17
|
-
<li>
|
|
18
|
-
<Link to=".">Start over</Link>
|
|
19
|
-
</li>
|
|
20
|
-
<li>
|
|
21
|
-
<Link to="one">
|
|
22
|
-
Param: <i>one</i>
|
|
23
|
-
</Link>
|
|
24
|
-
</li>
|
|
25
|
-
<li>
|
|
26
|
-
<Link to="two">
|
|
27
|
-
Param: <i>two</i>
|
|
28
|
-
</Link>
|
|
29
|
-
</li>
|
|
30
|
-
<li>
|
|
31
|
-
<Link to="this-record-does-not-exist">This will be a 404</Link>
|
|
32
|
-
</li>
|
|
33
|
-
<li>
|
|
34
|
-
<Link to="shh-its-a-secret">And this will be 401 Unauthorized</Link>
|
|
35
|
-
</li>
|
|
36
|
-
<li>
|
|
37
|
-
<Link to="kaboom">This one will throw an error</Link>
|
|
38
|
-
</li>
|
|
39
|
-
</ul>
|
|
40
|
-
</aside>
|
|
41
|
-
</div>
|
|
42
|
-
);
|
|
43
|
-
}
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* Whoa whoa whoa, wait a sec...why are we overriding global CSS selectors?
|
|
3
|
-
* Isn't that kind of scary? How do we know this won't have side effects?
|
|
4
|
-
*
|
|
5
|
-
* In Remix, CSS that is included in a route file will *only* show up on that
|
|
6
|
-
* route (and for nested routes, its children). When the user navigates away
|
|
7
|
-
* from that route the CSS files linked from those routes will be automatically
|
|
8
|
-
* unloaded, making your styles much easier to predict and control.
|
|
9
|
-
*
|
|
10
|
-
* Read more about styling routes in the docs:
|
|
11
|
-
* https://remix.run/guides/styling
|
|
12
|
-
*/
|
|
13
|
-
|
|
14
|
-
:root {
|
|
15
|
-
--color-foreground: hsl(0, 0%, 7%);
|
|
16
|
-
--color-background: hsl(56, 100%, 50%);
|
|
17
|
-
--color-links: hsl(345, 56%, 39%);
|
|
18
|
-
--color-links-hover: hsl(345, 51%, 49%);
|
|
19
|
-
--color-border: rgb(184, 173, 20);
|
|
20
|
-
--font-body: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas,
|
|
21
|
-
Liberation Mono, Courier New, monospace;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
.about__intro {
|
|
25
|
-
max-width: 500px;
|
|
26
|
-
}
|