marko-js / run Goto Github PK
View Code? Open in Web Editor NEWThe Marko application framework
License: MIT License
The Marko application framework
License: MIT License
A script tag with a relativesrc
path like <script src="./test.js"></script>
gets compiled into <script src="[CONTENTS OF test.js]"></script>
NOTE: That does not happen with absolute paths: /js/script.js
or full URLs
Marko does nothing and the script tag remains "as is". I was caught totally by surprise that Marko/Run was doing anything at all with something I considered "plain html".
Ok, nothing in Marko is plain HTML... but usually boundaries and side effects are clear. In this case, I don't know what's going on here.
Create a new Run project
Create a src/routes/test.js
file
export default "the script content";
Add <script src="./test.js"></script>
to +layout.marko
Build
This is easier to see when using the static adapter...
But it happens with the default adapter too
Allow specifying routes which optionally match a segment.
What should the directory name syntax be?
(optional)
[[optional]]
Should we support optional static segments?
Marko runs on port 3000 by default, but (for reasons) my work machine has another service running on that port. Is there a config file that I've not yet created?
I went and looked at the Vite docs. I tried setting it via Vite config, but Marko then errored with Unable to resolve @marko/run options
.
I've managed to get it working by running npm run dev -- --port 3123
, but I don't want to have to type that every time.
Specifying the "expires" attribute when setting a cookie, emits two "set-cookie" headers instead of one single header with the designated expiry. More specifically, a header like this:
set-cookie: flavor=chocolate%20chip; Expires=Wed, 23 Feb 2022 17:17:53 GMT
Gets converted into this:
set-cookie: flavor=chocolate%20chip; Expires=Wed
set-cookie: 23 Feb 2022 17:17:53 GMT
It looks like the culprit is https://github.com/marko-js/run/blob/main/packages/run/src/adapter/middleware.ts#L185
Currently when you add two files that match the same path, the only way to get rid of the error is to restart the dev server.
run/packages/adapters/netlify/src/index.ts
Line 186 in f2fbe82
Apart from the obvious typo, it should actually be "functions" as per docs on https://cli.netlify.com/commands/dev/
The imported virtual paths for routable files are like /__marko-run__route__notes__$__comments.marko
which causes the assets build by Rollup to be something like __marko-run__route__notes__$__comments.marko-8411b555.js
.
We should at the very least strip off the __marko-run__route__
portion and potentially find a way to simplify names for deep paths.
@marko/[email protected]
@marko@5.26.1
Create a +middleware.test.ts
file next to +middleware.ts
.
After that, the generated .marko-run/routes.d.ts
is changed like this:
- declare module "../src/routes/+middleware" {
+ declare module "../src/routes/+middleware.test" {
namespace MarkoRun {
export { NotHandled, NotMatched, GetPaths, PostPaths, GetablePath, GetableHref, PostablePath, PostableHref, Platform };
export type Route = Run.Routes["/path"];
export type Context = Run.MultiRouteContext<Route>;
export type Handler = Run.HandlerLike<Route>;
export const route: Run.HandlerTypeFn<Route>;
}
}
The module name shouldn't change to .../+middleware.test
https://github.com/Jack5079/marko-slug-bug
<!-- +page.marko -->
<a href="/path">path</a>
<!-- $+page.marko -->
<h1>here be $</h1>
Results in
dist
├── $
│ └── index.html
├── 404
│ └── index.html
├── path
│ └── index.html
└── index.html
But it should be:
dist
├── 404
│ └── index.html
├── path
│ └── index.html
└── index.html
As of "@marko/run": "^0.1.6"
on build or dev commands
https://github.com/marko-js/run/tree/main/examples/netlify
{
"name": "netlify-example",
"version": "0.0.1",
"type": "module",
"scripts": {
"build": "marko-run build",
"dev": "marko-run",
"start": "marko-run preview"
},
"devDependencies": {
"@marko/compiler": "^5.22.6",
"@marko/run": "^0.1.2",
"@marko/run-adapter-netlify": "^0.1.0",
"@marko/vite": "^2.3.7",
"@types/compression": "^1.7.2",
"@types/express": "^4.17.14",
"@types/mocha": "^9.1.1",
"@types/node": "^18.7.6",
"prettier": "^2.7.1",
"typescript": "^4.7.4",
"vite": "^4.1.4"
},
"dependencies": {
"marko": "^5.22.4"
}
}
> marko-run build
[vite]: Rollup failed to resolve import ".pnpm/@[email protected]_w2m7nasiukek3x36vgnrkkf62e/node_modules/@marko/vite/dist/components/vite.marko" from "marko-run-netlify/__marko-run__special__500.marko?marko-server-entry".
This is most likely unintended because it can break your application at runtime.
If you do want to externalize this module explicitly add it to
`build.rollupOptions.external`
marko-run-netlify/node_modules/.pnpm/[email protected]_@[email protected]/node_modules/vite/dist/node/chunks/dep-24daf00c.js:46525
throw new Error(`[vite]: Rollup failed to resolve import "${exporter}" from "${id}".\n` +
^
Error: [vite]: Rollup failed to resolve import ".pnpm/@[email protected]_w2m7nasiukek3x36vgnrkkf62e/node_modules/@marko/vite/dist/components/vite.marko" from "marko-run-netlify/__marko-run__special__500.marko?marko-server-entry".
This is most likely unintended because it can break your application at runtime.
If you do want to externalize this module explicitly add it to
`build.rollupOptions.external`
at viteWarn (marko-run-netlify/node_modules/.pnpm/[email protected]_@[email protected]/node_modules/vite/dist/node/chunks/dep-24daf00c.js:46525:23)
at onRollupWarning (marko-run-netlify/node_modules/.pnpm/[email protected]_@[email protected]/node_modules/vite/dist/node/chunks/dep-24daf00c.js:46549:9)
at onwarn (marko-run-netlify/node_modules/.pnpm/[email protected]_@[email protected]/node_modules/vite/dist/node/chunks/dep-24daf00c.js:46296:13)
at Object.onwarn (marko-run-netlify/node_modules/.pnpm/[email protected]/node_modules/rollup/dist/es/shared/node-entry.js:25280:13)
at ModuleLoader.handleInvalidResolvedId (marko-run-netlify/node_modules/.pnpm/[email protected]/node_modules/rollup/dist/es/shared/node-entry.js:23915:26)
at marko-run-netlify/node_modules/.pnpm/[email protected]/node_modules/rollup/dist/es/shared/node-entry.js:23875:26 {
watchFiles: [
....
]
{
"name": "marko-meta",
"description": "The default Marko starter app",
"version": "1.0.0",
"type": "module",
"dependencies": {
"marko": "^5"
},
"devDependencies": {
"@marko/run": "^0.1.6",
"@marko/run-adapter-netlify": "^0.1.3",
"vite": "^4.3.1"
},
"private": true,
"scripts": {
"build": "marko-run build",
"dev": "marko-run",
"start": "marko-run preview"
}
}
marko-run
10:43:01 PM [vite] Error when evaluating SSR module /node_modules/.pnpm/[email protected]/node_modules/undici/index.js:
|- ReferenceError: require is not defined
at eval (/node_modules/.pnpm/[email protected]/node_modules/undici/index.js:5:16)
at instantiateModule (marko-meta/node_modules/.pnpm/[email protected]/node_modules/vite/dist/node/chunks/dep-24daf00c.js:54351:15)
10:43:02 PM [vite] Error when evaluating SSR module @marko/run/adapter/middleware: failed to import "/node_modules/.pnpm/[email protected]/node_modules/undici/index.js"
|- ReferenceError: require is not defined
at eval (/node_modules/.pnpm/[email protected]/node_modules/undici/index.js:5:16)
at instantiateModule (marko-meta/node_modules/.pnpm/[email protected]/node_modules/vite/dist/node/chunks/dep-24daf00c.js:54351:15)
node:internal/process/promises:288
triggerUncaughtException(err, true /* fromPromise */);
^
ReferenceError: require is not defined
at eval (/node_modules/.pnpm/[email protected]/node_modules/undici/index.js:5:16)
at instantiateModule (marko-meta/node_modules/.pnpm/[email protected]/node_modules/vite/dist/node/chunks/dep-24daf00c.js:54351:15)
Node.js v18.9.0
ELIFECYCLE Command failed with exit code 1.
I use the +layout.marko file for all pages with routes. Given that the +404.marko and +500.marko files are living as standalone files in the root of the folder I'm not able to pull any metadata into these pages as I can with normal routes.
My +layout.marko looks like this
<!DOCTYPE html>
<html dir='ltr' lang=($global.meta.pageLanguage)>
<head>
<meta charset='UTF-8'>
...
<title>${$global.meta.pageTitle}</title>
</head>
<body>
<${input.renderBody}/>
</body>
</html>
Is there something I oversee?
From my point of view it would be nice to see the 404 and 500 pages live in special folders named _404
and _500
where files like +page.marko
and +meta.*
find a place.
Throw any error in any middleware/handler, and you get the custom +500.marko
page, but the HTTP status is 404.
@marko/[email protected]
Deploying to Netlify using the Netlify adapter. This error appears on a page that has a +middleware to fetch data from an external API. The error only appears when I visit the page many times in quick succession. I can get this error consistently by refreshing the page many times in a row.
The confusing part is the +middleware in question is wrapped in a try/catch, and the catch is simply returning a response with a 500 error code (which is what I want to happen). The Netlify logs do not point to any specific line or anything that is causing the error so I am just assuming the error is originating from this +middleware. Below is the error that I see in the Netlify logs.
@marko/[email protected]
@marko@5.26.4
In handler or middleware, I want to be able to append many Set-Cookie
headers to the response:
response.headers.append('Set-Cookie', 'alpha=alpha');
response.headers.append('Set-Cookie', 'beta=beta');
response.headers.append('Set-Cookie', 'charlie=charlie');
There are multiple Set-Cookie
entries for all 3 cookies in the response header.
Only the last appended cookie shows up in the response headers ("charlie" in this example):
$ curl -v http://localhost:3000
* Trying 127.0.0.1:3000...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 3000 (#0)
> GET / HTTP/1.1
> Host: localhost:3000
> User-Agent: curl/7.68.0
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< Access-Control-Allow-Origin: *
< content-type: text/html;charset=UTF-8
< middleware: test1, test2
< set-cookie: charlie=charlie
< Date: Mon, 19 Jun 2023 07:03:27 GMT
< Connection: keep-alive
< Keep-Alive: timeout=5
< Transfer-Encoding: chunked
Strangely if I set all cookies in 1 Set-Cookie
like this:
response.headers.append('Set-Cookie', 'alpha=alpha,beta=beta,charlie=charlie');
Then the response headers are correct:
curl -v http://localhost:3000
* Trying 127.0.0.1:3000...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 3000 (#0)
> GET / HTTP/1.1
> Host: localhost:3000
> User-Agent: curl/7.68.0
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< Access-Control-Allow-Origin: *
< content-type: text/html;charset=UTF-8
< middleware: test1, test2
< set-cookie: alpha=alpha
< set-cookie: beta=beta
< set-cookie: charlie=charlie
< Date: Mon, 19 Jun 2023 07:05:17 GMT
< Connection: keep-alive
< Keep-Alive: timeout=5
< Transfer-Encoding: chunked
Setup a Marko Run project. Add this middleware:
import { NextFunction } from "@marko/run";
export default async function(context: MarkoRun.Context, next: NextFunction) {
const response = await next();
response.headers.append('Set-Cookie', 'alpha=alpha');
response.headers.append('Set-Cookie', 'beta=beta');
response.headers.append('Set-Cookie', 'charlie=charlie');
response.headers.append('Middleware', 'test1')
response.headers.append('Middleware', 'test2')
console.log('Set-Cookie: ', response.headers.get('Set-Cookie')) // output: "Set-Cookie: alpha=alpha, beta=beta, charlie=charlie"
return response;
}
Let's say I want to fetch products from the database.
that's the old way:
import express from "express";
import markoPlugin from "@marko/express";
import template from "./template.marko";
const app = express();
app.use(markoPlugin()); // Enables `res.marko(template, input)`
app.get("/", function (req, res) {
const products = req.db.find(...)
res.marko(template, {
products
});
});
app.listen(8080);
What's the proper way to do the same in marko/run?
I tried something like this in +handler.js
export const GET: MarkoRun.Handler = ({ request, platform }, next) => {
platform.response.locals = {
data: Math.random()
}
next()
};
and then in +page.marko
<h1>${$global.platform.response.locals.data}</h1>
but i'm not sure it's the best way.
src
folder using Windows file path formatroutes.d.ts
file that needs to be committed.gitignore
file, so this is an issue each time changes are committed and pulled down between developers on different devicesThe expected behavior is that there shouldn't be a change on line 18 of the routes.d.ts
file that requires a commit after running the build command due to a change in the src
path when the project is regenerated on Mac OS or Windows.
\src\
folder to /src/
on line 18 on the routes.d.ts
file requiring the changes be committed."..\src\routes/" {
Windows file path:
Mac OS file path:
Currently unclear on the possible fix, but we were looking at pathPrefix
in the renderRouteTypeInfo
function within run/src/vite/codegen/index.ts
as a potential cause of this issue
"packageManager": "[email protected]",
"dependencies": {
"@manypkg/cli": "^0.20.0",
"turbo": "^1.9.3"
},
"dependencies": {
"marko": "^5.25.11"
},
"devDependencies": {
"@marko/run": "^0.1.9",
N/A
If I add a tsconfig.json, I need to put roughly the following in it:
{
"compilerOptions": {
"strict": true,
"lib": ["DOM", "DOM.Iterable", "ES6"],
"esModuleInterop": true,
"resolveJsonModule": true
},
"exclude": ["node_modules", "dist"],
"include": [".marko-run/routes.d.ts", "src"]
}
If I omit "lib", I'll get TS errors telling me that Promise isn't defined. (You can use something more aggressive than ES2019 if you're targeting modern browsers, but ES6 happens to be the minimum for Promise.)
If I omit either "esModuleInterop" or "resolveJsonModule", I see type errors in the example project, because the file generated in .marko-run/routes.d.ts
isn't parsed by typescript, so none of my per-route types seem to be visible in VS Code.
I'm going to suggest adding an example tsconfig.json file?
edit: I thought these were needed but they actually aren't:
"allowSyntheticDefaultImports": true,
"moduleResolution": "node",
npm run dev
results in the following Error
✘ [ERROR] Could not resolve "@marko/runtime-tags/debug/dom"
node_modules/marko/src/runtime/helpers/tags-compat/dom-debug.mjs:1:23:
1 │ import { compat } from "@marko/runtime-tags/debug/dom";
╵ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Attempting to visit local server will then produce:
[Error: ENOENT: no such file or directory, open '.../node_modules/@marko/run/.cache/explorer/data.json'
Rendered by getRoutes():
at _marko_template$5._._marko_renderer.t (file:///.../node_modules/@marko/run-explorer/.app/index.mjs:506:3)
at file:///.../node_modules/@marko/run-explorer/.app/index.mjs:694:5
at _marko_template$6._._marko_renderer.t (file:///.../node_modules/@marko/run-explorer/.app/index.mjs:377:3)] {
errno: -2,
code: 'ENOENT',
syscall: 'open',
path: '/.../node_modules/@marko/run/.cache/explorer/data.json'
}
Realize this is an issue further up the chain, but started here, so heads up.
In marko/[email protected]
, I have a route aaa.$aId.(,bbb.$bId).(,ccc.$cId)+page.marko
in order to support the URL structure:
/aaa/$aid/ccc/$cid
/aaa/$aid/bbb/$bid/ccc/$cid
/aaa/$aid/bbb/$bid
/aaa/$aid
This results in 4 permutations generated in .marko-run/routes.d.ts
below, which confuses the typechecking of $global.params
. Deleting the 3 shorter permutations calms Typescript down.
declare module "../src/routes/aaa.$aid.(,bbb.$bid).(,ccc.$cid)+page.marko" {
namespace MarkoRun {
export { NotHandled, NotMatched, GetPaths, PostPaths, GetablePath, GetableHref, PostablePath, PostableHref, Platform };
export type Route = Run.Routes["/aaa/:aid"];
export type Context = Run.MultiRouteContext<Route> & Marko.Global;
export type Handler = Run.HandlerLike<Route>;
export const route: Run.HandlerTypeFn<Route>;
}
}
declare module "../src/routes/aaa.$aid.(,bbb.$bid).(,ccc.$cid)+page.marko" {
namespace MarkoRun {
export { NotHandled, NotMatched, GetPaths, PostPaths, GetablePath, GetableHref, PostablePath, PostableHref, Platform };
export type Route = Run.Routes["/aaa/:aid/bbb/:bid"];
export type Context = Run.MultiRouteContext<Route> & Marko.Global;
export type Handler = Run.HandlerLike<Route>;
export const route: Run.HandlerTypeFn<Route>;
}
}
declare module "../src/routes/aaa.$aid.(,bbb.$bid).(,ccc.$cid)+page.marko" {
namespace MarkoRun {
export { NotHandled, NotMatched, GetPaths, PostPaths, GetablePath, GetableHref, PostablePath, PostableHref, Platform };
export type Route = Run.Routes["/aaa/:aid/bbb/:bid/ccc/:cid"];
export type Context = Run.MultiRouteContext<Route> & Marko.Global;
export type Handler = Run.HandlerLike<Route>;
export const route: Run.HandlerTypeFn<Route>;
}
}
declare module "../src/routes/aaa.$aid.(,bbb.$bid).(,ccc.$cid)+page.marko" {
namespace MarkoRun {
export { NotHandled, NotMatched, GetPaths, PostPaths, GetablePath, GetableHref, PostablePath, PostableHref, Platform };
export type Route = Run.Routes["/aaa/:aid/ccc/:cid"];
export type Context = Run.MultiRouteContext<Route> & Marko.Global;
export type Handler = Run.HandlerLike<Route>;
export const route: Run.HandlerTypeFn<Route>;
}
}
Versions:
Simply create an empty .d.ts
file like this:
declare module "@marko/run" {
}
Then Marko types are broken everywhere:
// In handler:
import { NextFunction } from "@marko/run"; // Module '"@marko/run"' has no exported member 'NextFunction'.
// in Marko file:
$ const foo = $global.url.searchParams; // '$global.url' is of type 'unknown'.
Generated types are broken, too:
Currently meta files +meta.json
needs to exist per +page.marko
. We can't cascade the same meta file for an entire sub-tree of routes at once. Such a capability could be quite useful to set default meta, and only overriding/augmenting where it makes sense. Perhaps something like https://www.11ty.dev/docs/data-cascade/
Can we have such a feature?
As discussed on Discord, wanted to document it here too
Currently you can extend the Context type as such:
declare module "@marko/run" {
interface ContextExtensions {
platform: WHATEVER
}
}
When considering adding support for Cloudflare, it would be very helpful to be able to extend the Platform type in a similar way (ie maybe through a PlatformExtensions
interface). Other adapters might also benefit, but particularly for Cloudflare this would allow for the end-user to specify their service binding types within their app code (as this would not be known ahead of time to the adapter code)
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.