@modern-js/plugin-data-loader 0.0.0-next-20221104140607

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.
@@ -0,0 +1,241 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.handleRequest = exports.getPathWithoutEntry = exports.default = void 0;
7
+ exports.isRedirectResponse = isRedirectResponse;
8
+ exports.isResponse = isResponse;
9
+
10
+ var _node = require("@remix-run/node");
11
+
12
+ var _reactRouterDom = require("react-router-dom");
13
+
14
+ function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
15
+
16
+ function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
17
+
18
+ function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; }
19
+
20
+ function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; }
21
+
22
+ function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
23
+
24
+ // Polyfill Web Fetch API
25
+ (0, _node.installGlobals)();
26
+ const LOADER_SEARCH_PARAM = '_loader';
27
+ const redirectStatusCodes = new Set([301, 302, 303, 307, 308]);
28
+
29
+ function isRedirectResponse(response) {
30
+ return redirectStatusCodes.has(response.status);
31
+ }
32
+
33
+ function isResponse(value) {
34
+ return value != null && typeof value.status === 'number' && typeof value.statusText === 'string' && typeof value.headers === 'object' && typeof value.body !== 'undefined';
35
+ }
36
+
37
+ const json = (data, init = {}) => {
38
+ const responseInit = typeof init === 'number' ? {
39
+ status: init
40
+ } : init;
41
+ const headers = new Headers(responseInit.headers);
42
+
43
+ if (!headers.has('Content-Type')) {
44
+ headers.set('Content-Type', 'application/json; charset=utf-8');
45
+ }
46
+
47
+ return new _node.Response(JSON.stringify(data), _objectSpread(_objectSpread({}, responseInit), {}, {
48
+ headers
49
+ }));
50
+ }; // TODO: 添加 context
51
+
52
+
53
+ const callRouteLoader = async ({
54
+ routeId,
55
+ loader,
56
+ params,
57
+ request,
58
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
59
+ loadContext
60
+ }) => {
61
+ if (!loader) {
62
+ throw new Error(`You made a ${request.method} request to ${request.url} but did not provide ` + `a default component or \`loader\` for route "${routeId}", ` + `so there is no way to handle the request.`);
63
+ }
64
+
65
+ let result;
66
+
67
+ try {
68
+ result = await loader({
69
+ request,
70
+ params
71
+ });
72
+ } catch (error) {
73
+ if (!isResponse(error)) {
74
+ throw error;
75
+ }
76
+
77
+ result = error;
78
+ }
79
+
80
+ if (result === undefined) {
81
+ throw new Error(`You defined a loader for route "${routeId}" but didn't return ` + `anything from your \`loader\` function. Please return a value or \`null\`.`);
82
+ }
83
+
84
+ return isResponse(result) ? result : json(result);
85
+ };
86
+
87
+ const createLoaderHeaders = requestHeaders => {
88
+ const headers = new Headers();
89
+
90
+ for (const [key, values] of Object.entries(requestHeaders)) {
91
+ if (values) {
92
+ if (Array.isArray(values)) {
93
+ for (const value of values) {
94
+ headers.append(key, value);
95
+ }
96
+ } else {
97
+ headers.set(key, values);
98
+ }
99
+ }
100
+ }
101
+
102
+ return headers;
103
+ };
104
+
105
+ const createLoaderRequest = context => {
106
+ const origin = `${context.protocol}://${context.host}`; // eslint-disable-next-line node/no-unsupported-features/node-builtins, node/prefer-global/url
107
+
108
+ const url = new URL(context.url, origin);
109
+ const controller = new AbortController();
110
+ const init = {
111
+ method: context.method,
112
+ headers: createLoaderHeaders(context.headers),
113
+ signal: controller.signal
114
+ };
115
+ return new Request(url.href, init);
116
+ };
117
+
118
+ const sendLoaderResponse = async (res, nodeResponse) => {
119
+ res.statusMessage = nodeResponse.statusText;
120
+ res.statusCode = nodeResponse.status;
121
+
122
+ for (const [key, value] of nodeResponse.headers.entries()) {
123
+ res.setHeader(key, value);
124
+ }
125
+
126
+ if (nodeResponse.body) {
127
+ await (0, _node.writeReadableStreamToWritable)(nodeResponse.body, res);
128
+ } else {
129
+ res.end();
130
+ }
131
+ };
132
+
133
+ const getPathWithoutEntry = (pathname, entryPath) => {
134
+ if (entryPath === '/') {
135
+ return pathname;
136
+ }
137
+
138
+ return pathname.replace(entryPath, '');
139
+ };
140
+
141
+ exports.getPathWithoutEntry = getPathWithoutEntry;
142
+
143
+ const matchEntry = (pathname, entries) => {
144
+ return entries.find(entry => pathname.startsWith(entry.urlPath));
145
+ };
146
+
147
+ const handleRequest = async ({
148
+ context,
149
+ serverRoutes,
150
+ distDir
151
+ }) => {
152
+ const {
153
+ method,
154
+ query
155
+ } = context;
156
+ const routeId = query[LOADER_SEARCH_PARAM];
157
+
158
+ if (!routeId || method.toLowerCase() !== 'get') {
159
+ return;
160
+ }
161
+
162
+ const entry = matchEntry(context.path, serverRoutes);
163
+
164
+ if (!entry) {
165
+ return;
166
+ }
167
+
168
+ const {
169
+ routes
170
+ } = await Promise.resolve(`${distDir}/loader-routes/${entry.entryName}`).then(s => _interopRequireWildcard(require(s)));
171
+
172
+ if (!routes) {
173
+ return;
174
+ }
175
+
176
+ const {
177
+ res
178
+ } = context;
179
+ const pathname = getPathWithoutEntry(context.path, entry.urlPath);
180
+ const matches = (0, _reactRouterDom.matchRoutes)(routes, pathname);
181
+
182
+ if (!matches) {
183
+ res.statusCode = 403;
184
+ res.end(`Route ${pathname} was not matched`);
185
+ return;
186
+ }
187
+
188
+ const match = matches === null || matches === void 0 ? void 0 : matches.find(match => match.route.id === routeId);
189
+
190
+ if (!match) {
191
+ res.statusCode = 403;
192
+ res.end(`Route ${routeId} does not match URL ${context.path}`);
193
+ return;
194
+ }
195
+
196
+ const request = createLoaderRequest(context);
197
+ let response;
198
+
199
+ try {
200
+ response = await callRouteLoader({
201
+ loader: match.route.loader,
202
+ routeId: match.route.id,
203
+ params: match.params,
204
+ request,
205
+ loadContext: {}
206
+ }); // TODO: 处理 redirect
207
+ } catch (error) {
208
+ const message = String(error);
209
+ response = new _node.Response(message, {
210
+ status: 500,
211
+ headers: {
212
+ 'Content-Type': 'text/plain'
213
+ }
214
+ });
215
+ }
216
+
217
+ sendLoaderResponse(res, response);
218
+ };
219
+
220
+ exports.handleRequest = handleRequest;
221
+
222
+ var _default = () => ({
223
+ name: '@modern-js/plugin-data-loader',
224
+ setup: () => ({
225
+ preparebeforeRouteHandler({
226
+ serverRoutes,
227
+ distDir
228
+ }) {
229
+ return async context => {
230
+ return handleRequest({
231
+ serverRoutes,
232
+ distDir,
233
+ context
234
+ });
235
+ };
236
+ }
237
+
238
+ })
239
+ });
240
+
241
+ exports.default = _default;
@@ -0,0 +1,67 @@
1
+ import _regeneratorRuntime from "@babel/runtime/helpers/esm/regeneratorRuntime";
2
+ import _asyncToGenerator from "@babel/runtime/helpers/esm/asyncToGenerator";
3
+ // eslint-disable-next-line eslint-comments/disable-enable-pair
4
+
5
+ /* eslint-disable node/prefer-global/url */
6
+ // eslint-disable-next-line eslint-comments/disable-enable-pair
7
+
8
+ /* eslint-disable node/no-unsupported-features/node-builtins */
9
+ import { compile } from 'path-to-regexp';
10
+ export var getRequestUrl = function getRequestUrl(_ref) {
11
+ var params = _ref.params,
12
+ request = _ref.request,
13
+ routeId = _ref.routeId;
14
+ var url = new URL(request.url);
15
+ var toPath = compile(url.pathname, {
16
+ encode: encodeURIComponent
17
+ });
18
+ var newPathName = toPath(params);
19
+ url.pathname = newPathName;
20
+ url.searchParams.append('_loader', routeId);
21
+ return url;
22
+ };
23
+ export var createRequest = function createRequest(routeId) {
24
+ var method = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'get';
25
+ return /*#__PURE__*/function () {
26
+ var _ref3 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee(_ref2) {
27
+ var params, request, url, res;
28
+ return _regeneratorRuntime().wrap(function _callee$(_context) {
29
+ while (1) {
30
+ switch (_context.prev = _context.next) {
31
+ case 0:
32
+ params = _ref2.params, request = _ref2.request;
33
+ url = getRequestUrl({
34
+ params: params,
35
+ request: request,
36
+ routeId: routeId
37
+ });
38
+ _context.prev = 2;
39
+ _context.next = 5;
40
+ return fetch(url, {
41
+ method: method,
42
+ signal: request.signal
43
+ });
44
+
45
+ case 5:
46
+ res = _context.sent;
47
+ return _context.abrupt("return", res);
48
+
49
+ case 9:
50
+ _context.prev = 9;
51
+ _context.t0 = _context["catch"](2);
52
+ console.error(_context.t0);
53
+ throw _context.t0;
54
+
55
+ case 13:
56
+ case "end":
57
+ return _context.stop();
58
+ }
59
+ }
60
+ }, _callee, null, [[2, 9]]);
61
+ }));
62
+
63
+ return function (_x) {
64
+ return _ref3.apply(this, arguments);
65
+ };
66
+ }();
67
+ };
@@ -0,0 +1,13 @@
1
+ import path from 'path';
2
+ import { getRouteId } from '@modern-js/utils';
3
+ export var generateClient = function generateClient(_ref) {
4
+ var basedir = _ref.basedir,
5
+ filename = _ref.filename;
6
+ // TODO: loader 的约定变更
7
+ var componentPath = path.join(path.dirname(filename), 'layout.ts');
8
+ var routeId = getRouteId(componentPath, basedir);
9
+ var requestCreatorPath = path.join(__dirname, './create-request').replace('node', 'treeshaking').replace(/\\/g, '/');
10
+ var importCode = "\n import { createRequest } from '".concat(requestCreatorPath, "';\n ");
11
+ var requestCode = "\n const loader = createRequest('".concat(routeId, "');\n export default loader;\n export { loader };\n ");
12
+ return "\n ".concat(importCode, "\n ").concat(requestCode, "\n ");
13
+ };
@@ -0,0 +1,46 @@
1
+ import _regeneratorRuntime from "@babel/runtime/helpers/esm/regeneratorRuntime";
2
+ import _asyncToGenerator from "@babel/runtime/helpers/esm/asyncToGenerator";
3
+ // eslint-disable-next-line eslint-comments/disable-enable-pair
4
+
5
+ /* eslint-disable @babel/no-invalid-this */
6
+ import { generateClient } from "./generate-client";
7
+ export default function loader(_x) {
8
+ return _loader.apply(this, arguments);
9
+ }
10
+
11
+ function _loader() {
12
+ _loader = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee(source) {
13
+ var _this$_compiler;
14
+
15
+ var target, options, code;
16
+ return _regeneratorRuntime().wrap(function _callee$(_context) {
17
+ while (1) {
18
+ switch (_context.prev = _context.next) {
19
+ case 0:
20
+ this.cacheable();
21
+ target = (_this$_compiler = this._compiler) === null || _this$_compiler === void 0 ? void 0 : _this$_compiler.options.target;
22
+
23
+ if (!(target === 'node')) {
24
+ _context.next = 4;
25
+ break;
26
+ }
27
+
28
+ return _context.abrupt("return", source);
29
+
30
+ case 4:
31
+ options = this.getOptions();
32
+ code = generateClient({
33
+ basedir: options.routesDir,
34
+ filename: this.resourcePath
35
+ });
36
+ return _context.abrupt("return", code);
37
+
38
+ case 7:
39
+ case "end":
40
+ return _context.stop();
41
+ }
42
+ }
43
+ }, _callee, this);
44
+ }));
45
+ return _loader.apply(this, arguments);
46
+ }