@marko/run 0.4.5 → 0.4.6

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.
@@ -930,45 +930,6 @@ function writeRouteEntryHandler(writer, route, verb) {
930
930
  continuations.join();
931
931
  writer.writeBlockEnd("}");
932
932
  }
933
- function renderErrorRouter(error, options = {
934
- trailingSlashes: "RedirectWithout"
935
- }) {
936
- const writer = createStringWriter();
937
- writer.write(`
938
- // @marko/run/router
939
- import { createContext } from '${virtualFilePrefix}/runtime/internal';
940
- import errorPage from '${virtualFilePrefix}/${markoRunFilePrefix}error.marko${serverEntryQuery}';
941
-
942
- const error = new Error(\`${error.message}\`);
943
- error.name = '${error.name}';`);
944
- if (error.stack) {
945
- writer.write(`
946
- error.stack = \`${error.stack}\`;`);
947
- }
948
- writer.write(`
949
-
950
- globalThis.__marko_run__ = { match, fetch, invoke };
951
-
952
- export function match() {
953
- return { handler: errorPage, params: {}, meta: {}, path: '/*' }; // /$$
954
- }
955
-
956
- export async function invoke(route, request, platform, url) {
957
- const [context, buildInput] = createContext(route, request, platform, url);
958
- if (context.request.headers.get('Accept')?.includes('text/html')) {
959
- return new Response(errorPage.stream(buildInput({ error })), {
960
- status: 500,
961
- headers: { "content-type": "text/html;charset=UTF-8" },
962
- });
963
- }
964
- return new Response(error, {
965
- status: 500,
966
- });
967
- }
968
- `);
969
- renderFetch(writer, options);
970
- return writer.end();
971
- }
972
933
  function renderRouter(routes, options = {
973
934
  trailingSlashes: "RedirectWithout"
974
935
  }) {
@@ -1101,15 +1062,7 @@ export async function fetch(request, platform) {
1101
1062
  return await invoke(route, request, platform, url);
1102
1063
  } catch (error) {
1103
1064
  if (import.meta.env.DEV) {
1104
- let body;
1105
- if (error.cause) {
1106
- body = error.cause.stack || error.cause.message || error.cause;
1107
- } else {
1108
- body = error.stack || error.message || "Internal Server Error";
1109
- }
1110
- return new Response(body, {
1111
- status: 500
1112
- });
1065
+ throw error;
1113
1066
  }
1114
1067
  return new Response(null, {
1115
1068
  status: 500
@@ -1549,6 +1502,29 @@ function getExportIdentifiers(astProgramNode) {
1549
1502
  }
1550
1503
  return result;
1551
1504
  }
1505
+ function getViteSSRExportIdentifiers(astProgramNode, exportObjectName = "__vite_ssr_exports__") {
1506
+ const result = [];
1507
+ if (t.isProgram(astProgramNode)) {
1508
+ for (const node of astProgramNode.body) {
1509
+ if (t.isExpressionStatement(node)) {
1510
+ if (t.isAssignmentExpression(node.expression) && t.isMemberExpression(node.expression.left)) {
1511
+ const { object, property } = node.expression.left;
1512
+ if (t.isIdentifier(object) && object.name === exportObjectName && t.isIdentifier(property)) {
1513
+ result.push(property.name);
1514
+ }
1515
+ } else if (t.isCallExpression(node.expression) && t.isMemberExpression(node.expression.callee)) {
1516
+ const {
1517
+ arguments: [arg0, arg1]
1518
+ } = node.expression;
1519
+ if (t.isIdentifier(arg0) && arg0.name === exportObjectName && (t.isStringLiteral(arg1) || "value" in arg1 && typeof arg1.value === "string")) {
1520
+ result.push(arg1.value);
1521
+ }
1522
+ }
1523
+ }
1524
+ }
1525
+ }
1526
+ return result;
1527
+ }
1552
1528
 
1553
1529
  // src/vite/utils/log.ts
1554
1530
  var import_node_zlib = __toESM(require("node:zlib"), 1);
@@ -1741,6 +1717,31 @@ process.once("beforeExit", (code) => {
1741
1717
  }
1742
1718
  });
1743
1719
 
1720
+ // src/adapter/utils.ts
1721
+ var import_supports_color = __toESM(require("supports-color"), 1);
1722
+ var import_kleur2 = __toESM(require("kleur"), 1);
1723
+ function stripAnsi(string) {
1724
+ return string.replace(
1725
+ /([\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><])/g,
1726
+ ""
1727
+ );
1728
+ }
1729
+ function cleanStack(stack) {
1730
+ return stack.split(/\n/).filter((l) => /^\s*at/.test(l)).join("\n");
1731
+ }
1732
+ function prepareError(err) {
1733
+ var _a;
1734
+ return {
1735
+ message: stripAnsi(err.message),
1736
+ stack: stripAnsi(cleanStack(err.stack || "")),
1737
+ id: err.id,
1738
+ frame: stripAnsi(err.frame || ""),
1739
+ plugin: err.plugin,
1740
+ pluginCode: (_a = err.pluginCode) == null ? void 0 : _a.toString(),
1741
+ loc: err.loc
1742
+ };
1743
+ }
1744
+
1744
1745
  // src/vite/plugin.ts
1745
1746
  var debug = (0, import_debug.default)("@marko/run");
1746
1747
  var __dirname = import_path3.default.dirname((0, import_url2.fileURLToPath)(__importMetaURL));
@@ -1764,11 +1765,10 @@ function markoRun(opts = {}) {
1764
1765
  let devServer;
1765
1766
  let routes;
1766
1767
  let routeData;
1767
- let extractVerbs;
1768
+ let getExportsFromFile;
1768
1769
  let resolvedConfig;
1769
1770
  let typesFile;
1770
- let isStale = true;
1771
- let isRendered = false;
1771
+ let seenErrors = /* @__PURE__ */ new Set();
1772
1772
  const virtualFiles = /* @__PURE__ */ new Map();
1773
1773
  let times = {
1774
1774
  routesBuild: 0,
@@ -1791,97 +1791,134 @@ function markoRun(opts = {}) {
1791
1791
  }
1792
1792
  }
1793
1793
  }
1794
- const buildVirtualFiles = single(async (render) => {
1795
- var _a;
1796
- const routerOptions = {
1797
- trailingSlashes: opts.trailingSlashes || "RedirectWithout"
1798
- };
1799
- try {
1800
- if (isStale) {
1794
+ let buildVirtualFilesResult;
1795
+ function buildVirtualFiles() {
1796
+ return buildVirtualFilesResult ?? (buildVirtualFilesResult = new Promise(async (resolve, reject) => {
1797
+ try {
1801
1798
  virtualFiles.clear();
1802
- isRendered = false;
1803
- const buildStartTime = performance.now();
1804
1799
  routes = await buildRoutes({
1805
1800
  walker: createFSWalker(resolvedRoutesDir),
1806
1801
  importPrefix: routesDir
1807
1802
  });
1808
- times.routesBuild = performance.now() - buildStartTime;
1809
1803
  if (!routes.list.length) {
1810
1804
  throw new Error("No routes generated");
1811
1805
  }
1812
- }
1813
- const renderStartTime = performance.now();
1814
- for (const route of routes.list) {
1815
- if (render && route.handler) {
1816
- route.handler.verbs = await extractVerbs(route.handler.filePath);
1817
- if (!route.handler.verbs.length) {
1818
- throw new Error(
1819
- `Did not find any valid exports in middleware entry file:'${route.handler.filePath}' - expected to find any of 'GET', 'POST', 'PUT' or 'DELETE'`
1806
+ for (const route of routes.list) {
1807
+ if (route.page) {
1808
+ virtualFiles.set(
1809
+ import_path3.default.posix.join(root, `${route.entryName}.marko`),
1810
+ ""
1820
1811
  );
1821
1812
  }
1813
+ virtualFiles.set(import_path3.default.posix.join(root, `${route.entryName}.js`), "");
1822
1814
  }
1823
- if (route.page) {
1815
+ for (const route of Object.values(routes.special)) {
1824
1816
  virtualFiles.set(
1825
1817
  import_path3.default.posix.join(root, `${route.entryName}.marko`),
1826
- render ? renderRouteTemplate(route) : ""
1818
+ ""
1827
1819
  );
1828
1820
  }
1829
- virtualFiles.set(
1830
- import_path3.default.posix.join(root, `${route.entryName}.js`),
1831
- render ? renderRouteEntry(route) : ""
1832
- );
1833
- }
1834
- for (const route of Object.values(routes.special)) {
1835
- virtualFiles.set(
1836
- import_path3.default.posix.join(root, `${route.entryName}.marko`),
1837
- render ? renderRouteTemplate(route) : ""
1838
- );
1821
+ if (routes.middleware.length) {
1822
+ virtualFiles.set(
1823
+ import_path3.default.posix.join(root, `${markoRunFilePrefix}middleware.js`),
1824
+ ""
1825
+ );
1826
+ }
1827
+ virtualFiles.set("@marko/run/router", "");
1828
+ resolve(routes);
1829
+ } catch (err) {
1830
+ reject(err);
1839
1831
  }
1840
- if (routes.middleware.length) {
1832
+ }));
1833
+ }
1834
+ let renderVirtualFilesResult;
1835
+ function renderVirtualFiles(context) {
1836
+ return renderVirtualFilesResult ?? (renderVirtualFilesResult = new Promise(async (resolve) => {
1837
+ var _a;
1838
+ const routerOptions = {
1839
+ trailingSlashes: opts.trailingSlashes || "RedirectWithout"
1840
+ };
1841
+ try {
1842
+ const routes2 = await buildVirtualFiles();
1843
+ for (const route of routes2.list) {
1844
+ if (route.handler) {
1845
+ const exports2 = await getExportsFromFile(
1846
+ context,
1847
+ route.handler.filePath
1848
+ );
1849
+ route.handler.verbs = [];
1850
+ for (const name of exports2) {
1851
+ const verb = name.toLowerCase();
1852
+ if (name === verb.toUpperCase() && httpVerbs.includes(verb)) {
1853
+ route.handler.verbs.push(verb);
1854
+ }
1855
+ }
1856
+ if (!route.handler.verbs.length) {
1857
+ context.warn(
1858
+ `Did not find any http verb exports in handler '${import_path3.default.relative(root, route.handler.filePath)}' - expected ${httpVerbs.map((v) => v.toUpperCase()).join(", ")}`
1859
+ );
1860
+ }
1861
+ }
1862
+ if (route.page) {
1863
+ virtualFiles.set(
1864
+ import_path3.default.posix.join(root, `${route.entryName}.marko`),
1865
+ renderRouteTemplate(route)
1866
+ );
1867
+ }
1868
+ virtualFiles.set(
1869
+ import_path3.default.posix.join(root, `${route.entryName}.js`),
1870
+ renderRouteEntry(route)
1871
+ );
1872
+ }
1873
+ for (const route of Object.values(routes2.special)) {
1874
+ virtualFiles.set(
1875
+ import_path3.default.posix.join(root, `${route.entryName}.marko`),
1876
+ renderRouteTemplate(route)
1877
+ );
1878
+ }
1879
+ if (routes2.middleware.length) {
1880
+ for (const middleware of routes2.middleware) {
1881
+ if (!(await getExportsFromFile(context, middleware.filePath)).includes("default")) {
1882
+ context.warn(
1883
+ `Did not find a default export in middleware '${import_path3.default.relative(root, middleware.filePath)}'`
1884
+ );
1885
+ }
1886
+ }
1887
+ virtualFiles.set(
1888
+ import_path3.default.posix.join(root, `${markoRunFilePrefix}middleware.js`),
1889
+ renderMiddleware(routes2.middleware)
1890
+ );
1891
+ }
1841
1892
  virtualFiles.set(
1842
- import_path3.default.posix.join(root, `${markoRunFilePrefix}middleware.js`),
1843
- render ? renderMiddleware(routes.middleware) : ""
1893
+ "@marko/run/router",
1894
+ renderRouter(routes2, routerOptions)
1844
1895
  );
1845
- }
1846
- virtualFiles.set(
1847
- "@marko/run/router",
1848
- render ? renderRouter(routes, routerOptions) : ""
1849
- );
1850
- times.routesRender = performance.now() - renderStartTime;
1851
- if (render) {
1852
- await writeTypesFile(routes);
1896
+ await writeTypesFile(routes2);
1853
1897
  if (adapter == null ? void 0 : adapter.routesGenerated) {
1854
1898
  await adapter.routesGenerated(
1855
- routes,
1899
+ routes2,
1856
1900
  new Map(virtualFiles.entries()),
1857
1901
  {
1858
1902
  buildTime: times.routesBuild,
1859
1903
  renderTime: times.routesRender
1860
1904
  }
1861
1905
  );
1906
+ if (!isBuild) {
1907
+ await ((_a = opts == null ? void 0 : opts.emitRoutes) == null ? void 0 : _a.call(opts, routes2.list));
1908
+ }
1862
1909
  }
1863
- if (!isBuild) {
1864
- await ((_a = opts == null ? void 0 : opts.emitRoutes) == null ? void 0 : _a.call(opts, routes.list));
1910
+ } catch (err) {
1911
+ if (isBuild) {
1912
+ throw err;
1865
1913
  }
1866
- isRendered = true;
1867
- }
1868
- } catch (err) {
1869
- if (isBuild) {
1870
- throw err;
1914
+ virtualFiles.set(
1915
+ "@marko/run/router",
1916
+ `throw ${JSON.stringify(prepareError(err))}`
1917
+ );
1871
1918
  }
1872
- console.error(err);
1873
- virtualFiles.set(
1874
- import_path3.default.posix.join(root, `${markoRunFilePrefix}error.marko`),
1875
- renderEntryTemplate(`${markoRunFilePrefix}error`, ["<dev-error-page>"])
1876
- );
1877
- virtualFiles.set(
1878
- "@marko/run/router",
1879
- renderErrorRouter(err, routerOptions)
1880
- );
1881
- isRendered = true;
1882
- }
1883
- isStale = false;
1884
- });
1919
+ resolve();
1920
+ }));
1921
+ }
1885
1922
  return [
1886
1923
  {
1887
1924
  name: `${PLUGIN_NAME_PREFIX}:pre`,
@@ -2041,29 +2078,44 @@ function markoRun(opts = {}) {
2041
2078
  } else {
2042
2079
  ssrEntryFiles = [];
2043
2080
  }
2081
+ const baseError = config2.logger.error;
2082
+ config2.logger.error = function(msg, options) {
2083
+ var _a;
2084
+ if (!((_a = options == null ? void 0 : options.error) == null ? void 0 : _a.message)) {
2085
+ baseError.call(this, msg, options);
2086
+ } else if (!seenErrors.has(options.error.message)) {
2087
+ seenErrors.add(options.error.message);
2088
+ console.error((0, import_vite.buildErrorMessage)(options.error));
2089
+ }
2090
+ };
2044
2091
  },
2045
2092
  configureServer(_server) {
2046
2093
  devServer = _server;
2047
2094
  devServer.watcher.on("all", async (type, filename) => {
2048
- const routableFileType = matchRoutableFile(import_path3.default.parse(filename).base);
2095
+ seenErrors.clear();
2096
+ const routableFileType = matchRoutableFile(
2097
+ import_path3.default.parse(filename).base
2098
+ );
2049
2099
  if (filename.startsWith(resolvedRoutesDir) && routableFileType) {
2050
- if (type === "add") {
2051
- isStale = true;
2052
- } else if (type === "unlink") {
2053
- isStale = true;
2054
- } else if (type === "change") {
2055
- if (routableFileType === RoutableFileTypes.Handler) {
2056
- isStale = true;
2057
- }
2058
- }
2059
- if (isStale) {
2060
- for (const id of virtualFiles.keys()) {
2061
- devServer.watcher.emit("change", id);
2062
- break;
2100
+ if (type === "add" || type === "unlink" || type === "change" && (routableFileType === RoutableFileTypes.Handler || routableFileType === RoutableFileTypes.Middleware)) {
2101
+ buildVirtualFilesResult = void 0;
2102
+ renderVirtualFilesResult = void 0;
2103
+ const module2 = devServer.moduleGraph.getModuleById(filename);
2104
+ const importers = module2 && getImporters(module2, filename);
2105
+ if (importers == null ? void 0 : importers.size) {
2106
+ for (const file of importers) {
2107
+ devServer.watcher.emit("change", file);
2108
+ }
2109
+ } else {
2110
+ for (const file of virtualFiles.keys()) {
2111
+ if (!file.endsWith(".marko")) {
2112
+ devServer.watcher.emit("change", file);
2113
+ }
2114
+ }
2063
2115
  }
2064
2116
  }
2065
2117
  }
2066
- });
2118
+ }).unwatch(typesDir + "/*");
2067
2119
  },
2068
2120
  async buildStart(_options) {
2069
2121
  if (isBuild && !isSSRBuild) {
@@ -2079,10 +2131,10 @@ function markoRun(opts = {}) {
2079
2131
  for (const { key, code } of routeData.files) {
2080
2132
  virtualFiles.set(key, code);
2081
2133
  }
2082
- isStale = false;
2083
- isRendered = true;
2134
+ buildVirtualFilesResult = Promise.resolve(routes);
2135
+ renderVirtualFilesResult = Promise.resolve();
2084
2136
  } else {
2085
- extractVerbs = isBuild ? getVerbsFromFileBuild.bind(null, this) : getVerbsFromFileDev.bind(null, devServer);
2137
+ getExportsFromFile = isBuild ? getExportsFromFileBuild : getExportsFromFileDev.bind(null, devServer);
2086
2138
  }
2087
2139
  },
2088
2140
  async resolveId(importee, importer) {
@@ -2095,8 +2147,8 @@ function markoRun(opts = {}) {
2095
2147
  importee = import_path3.default.resolve(root, "." + importee);
2096
2148
  }
2097
2149
  importee = normalizePath(importee);
2098
- if (isStale) {
2099
- await buildVirtualFiles(false);
2150
+ if (!buildVirtualFilesResult) {
2151
+ await buildVirtualFiles();
2100
2152
  }
2101
2153
  if (virtualFiles.has(importee)) {
2102
2154
  resolved = importee;
@@ -2113,11 +2165,13 @@ function markoRun(opts = {}) {
2113
2165
  if (id.endsWith(serverEntryQuery)) {
2114
2166
  id = id.slice(0, -serverEntryQuery.length);
2115
2167
  }
2168
+ if (!renderVirtualFilesResult) {
2169
+ await renderVirtualFiles(this);
2170
+ }
2116
2171
  if (virtualFiles.has(id)) {
2117
- if (isStale || !isRendered) {
2118
- await buildVirtualFiles(true);
2119
- }
2120
2172
  return virtualFiles.get(id);
2173
+ } else if (import_path3.default.basename(id).startsWith(markoRunFilePrefix) && /^\.(js|marko)$/.test(import_path3.default.extname(id))) {
2174
+ return "";
2121
2175
  }
2122
2176
  }
2123
2177
  },
@@ -2175,57 +2229,20 @@ function markoRun(opts = {}) {
2175
2229
  }
2176
2230
  ];
2177
2231
  }
2178
- async function getVerbsFromFileBuild(context, filePath) {
2179
- const verbs = [];
2232
+ async function getExportsFromFileBuild(context, filePath) {
2180
2233
  const result = await context.load({
2181
2234
  id: filePath,
2182
2235
  resolveDependencies: false
2183
2236
  });
2184
- if (result) {
2185
- const exportIds = getExportIdentifiers(result.ast);
2186
- for (const id of exportIds) {
2187
- const verb = id.toLowerCase();
2188
- if (id === verb.toUpperCase() && httpVerbs.includes(verb)) {
2189
- verbs.push(verb);
2190
- }
2191
- }
2192
- }
2193
- return verbs;
2237
+ return result ? getExportIdentifiers(result.ast) : [];
2194
2238
  }
2195
- async function getVerbsFromFileDev(devServer, filePath) {
2196
- const verbs = [];
2239
+ async function getExportsFromFileDev(devServer, context, filePath) {
2197
2240
  const result = await devServer.transformRequest(filePath, { ssr: true });
2198
- if (result && result.code) {
2199
- const verbMatchReg = /__vite_ssr_exports__,\s+["'](GET|POST|PUT|DELETE)["']/gi;
2200
- let match = verbMatchReg.exec(result.code);
2201
- while (match) {
2202
- const id = match[1];
2203
- const verb = id.toLowerCase();
2204
- if (httpVerbs.includes(verb)) {
2205
- if (id === verb.toUpperCase()) {
2206
- verbs.push(verb);
2207
- } else {
2208
- console.warn(
2209
- `Found export '${id}' in handler ${filePath} which is close to '${verb.toUpperCase()}'. Exported handlers need to be uppercase: GET, POST, PUT or DELETE.`
2210
- );
2211
- }
2212
- }
2213
- match = verbMatchReg.exec(result.code);
2214
- }
2241
+ if (result) {
2242
+ const ast = context.parse(result.code);
2243
+ return getViteSSRExportIdentifiers(ast);
2215
2244
  }
2216
- return verbs;
2217
- }
2218
- function single(fn) {
2219
- let promise;
2220
- return async (...args) => {
2221
- if (promise) {
2222
- return promise;
2223
- }
2224
- promise = fn(...args);
2225
- const result = await promise;
2226
- promise = void 0;
2227
- return result;
2228
- };
2245
+ return [];
2229
2246
  }
2230
2247
  async function globFileExists(root, pattern) {
2231
2248
  return (await (0, import_glob.glob)(pattern, { root })).length > 0;
@@ -2278,6 +2295,15 @@ function getEntryFileName(file) {
2278
2295
  const match = file && markoEntryFileRegex.exec(file);
2279
2296
  return match ? match[2] || "index" : void 0;
2280
2297
  }
2298
+ function getImporters(module2, fileName, seen = /* @__PURE__ */ new Set()) {
2299
+ for (const importer of module2.importers) {
2300
+ if (importer.id && !seen.has(importer.id)) {
2301
+ seen.add(importer.id);
2302
+ getImporters(importer, fileName, seen);
2303
+ }
2304
+ }
2305
+ return seen;
2306
+ }
2281
2307
 
2282
2308
  // src/vite/utils/server.ts
2283
2309
  var import_net = __toESM(require("net"), 1);