mirror of
https://github.com/dillonkearns/elm-pages-v3-beta.git
synced 2024-11-24 06:54:03 +03:00
Use esbuild to transpile port-data-source file, and watch for changes.
This commit is contained in:
parent
8a0eae997b
commit
8489f44a3d
24
examples/end-to-end/port-data-source.ts
Normal file
24
examples/end-to-end/port-data-source.ts
Normal file
@ -0,0 +1,24 @@
|
||||
import kleur from "kleur";
|
||||
kleur.enabled = true;
|
||||
|
||||
export async function environmentVariable(name) {
|
||||
const result = process.env[name];
|
||||
if (result) {
|
||||
return result;
|
||||
} else {
|
||||
throw `No environment variable called ${kleur
|
||||
.yellow()
|
||||
.underline(name)}\n\nAvailable:\n\n${Object.keys(process.env)
|
||||
.slice(0, 5)
|
||||
.join("\n")}`;
|
||||
}
|
||||
}
|
||||
|
||||
export async function hello(name) {
|
||||
await waitFor(1000);
|
||||
return `147 ${name}!!`;
|
||||
}
|
||||
|
||||
function waitFor(ms) {
|
||||
return new Promise((resolve) => setTimeout(resolve, ms));
|
||||
}
|
@ -24,6 +24,7 @@ const cookie = require("cookie");
|
||||
const busboy = require("busboy");
|
||||
const { createServer: createViteServer } = require("vite");
|
||||
const cliVersion = require("../../package.json").version;
|
||||
const esbuild = require("esbuild");
|
||||
|
||||
/**
|
||||
* @param {{ port: string; base: string; https: boolean; debug: boolean; }} options
|
||||
@ -137,6 +138,40 @@ async function start(options) {
|
||||
base: options.base,
|
||||
...viteConfig,
|
||||
});
|
||||
esbuild
|
||||
.build({
|
||||
entryPoints: ["./port-data-source"],
|
||||
entryNames: "[dir]/[name]-[hash]",
|
||||
|
||||
outdir: ".elm-pages/compiled-ports",
|
||||
assetNames: "[name]-[hash]",
|
||||
chunkNames: "chunks/[name]-[hash]",
|
||||
outExtension: { ".js": ".mjs" },
|
||||
|
||||
metafile: true,
|
||||
bundle: false,
|
||||
watch: true,
|
||||
plugins: [
|
||||
{
|
||||
name: "example",
|
||||
setup(build) {
|
||||
build.onEnd((result) => {
|
||||
global.portsFilePath = Object.keys(result.metafile.outputs)[0];
|
||||
|
||||
clients.forEach((client) => {
|
||||
client.response.write(`data: content.dat\n\n`);
|
||||
});
|
||||
});
|
||||
},
|
||||
},
|
||||
],
|
||||
})
|
||||
.then((result) => {
|
||||
console.log("Watching port-data-source...");
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error("Failed to start port-data-source watcher", error);
|
||||
});
|
||||
|
||||
const app = connect()
|
||||
.use(timeMiddleware())
|
||||
@ -288,6 +323,7 @@ async function start(options) {
|
||||
mode: "dev-server",
|
||||
pathname,
|
||||
serverRequest,
|
||||
portsFilePath: global.portsFilePath,
|
||||
});
|
||||
readyThread.worker.on("message", (message) => {
|
||||
if (message.tag === "done") {
|
||||
@ -379,6 +415,7 @@ async function start(options) {
|
||||
});
|
||||
|
||||
req.on("end", async function () {
|
||||
// TODO run render directly instead of in worker thread
|
||||
await runRenderThread(
|
||||
await reqToJson(req, body, requestTime),
|
||||
pathname,
|
||||
|
5
generator/src/hello.ts
Normal file
5
generator/src/hello.ts
Normal file
@ -0,0 +1,5 @@
|
||||
export function hello(): string {
|
||||
console.log("HELLLO!!!!");
|
||||
|
||||
return "Hello World!";
|
||||
}
|
@ -7,10 +7,11 @@ let Elm;
|
||||
|
||||
global.staticHttpCache = {};
|
||||
|
||||
async function run({ mode, pathname, serverRequest }) {
|
||||
async function run({ mode, pathname, serverRequest, portsFilePath }) {
|
||||
console.time(`${threadId} ${pathname}`);
|
||||
try {
|
||||
const renderResult = await renderer(
|
||||
portsFilePath,
|
||||
workerData.basePath,
|
||||
requireElm(mode),
|
||||
mode,
|
||||
|
@ -30,6 +30,7 @@ module.exports =
|
||||
* @returns
|
||||
*/
|
||||
async function run(
|
||||
portsFile,
|
||||
basePath,
|
||||
elmModule,
|
||||
mode,
|
||||
@ -49,6 +50,7 @@ module.exports =
|
||||
// we can provide a fake HTTP instead of xhr2 (which is otherwise needed for Elm HTTP requests from Node)
|
||||
XMLHttpRequest = {};
|
||||
const result = await runElmApp(
|
||||
portsFile,
|
||||
basePath,
|
||||
elmModule,
|
||||
mode,
|
||||
@ -71,6 +73,7 @@ module.exports =
|
||||
* @returns {Promise<({is404: boolean} & ( { kind: 'json'; contentJson: string} | { kind: 'html'; htmlString: string } | { kind: 'api-response'; body: string; }) )>}
|
||||
*/
|
||||
function runElmApp(
|
||||
portsFile,
|
||||
basePath,
|
||||
elmModule,
|
||||
mode,
|
||||
@ -161,7 +164,10 @@ function runElmApp(
|
||||
}
|
||||
} else if (fromElm.tag === "DoHttp") {
|
||||
const requestToPerform = fromElm.args[0];
|
||||
if (requestToPerform.url.startsWith("elm-pages-internal://")) {
|
||||
if (
|
||||
requestToPerform.url !== "elm-pages-internal://port" &&
|
||||
requestToPerform.url.startsWith("elm-pages-internal://")
|
||||
) {
|
||||
runInternalJob(
|
||||
app,
|
||||
mode,
|
||||
@ -171,7 +177,7 @@ function runElmApp(
|
||||
patternsToWatch
|
||||
);
|
||||
} else {
|
||||
runHttpJob(app, mode, requestToPerform, fs, hasFsAccess);
|
||||
runHttpJob(portsFile, app, mode, requestToPerform, fs, hasFsAccess);
|
||||
}
|
||||
} else if (fromElm.tag === "Errors") {
|
||||
foundErrors = true;
|
||||
@ -229,10 +235,18 @@ async function outputString(
|
||||
|
||||
/** @typedef { { head: any[]; errors: any[]; contentJson: any[]; html: string; route: string; title: string; } } Arg */
|
||||
|
||||
async function runHttpJob(app, mode, requestToPerform, fs, hasFsAccess) {
|
||||
async function runHttpJob(
|
||||
portsFile,
|
||||
app,
|
||||
mode,
|
||||
requestToPerform,
|
||||
fs,
|
||||
hasFsAccess
|
||||
) {
|
||||
pendingDataSourceCount += 1;
|
||||
try {
|
||||
const responseFilePath = await lookupOrPerform(
|
||||
portsFile,
|
||||
mode,
|
||||
requestToPerform,
|
||||
hasFsAccess
|
||||
|
@ -16,29 +16,36 @@ function requestToString(request) {
|
||||
/**
|
||||
* @param {Object} request
|
||||
*/
|
||||
function fullPath(request, hasFsAccess) {
|
||||
function fullPath(portsHash, request, hasFsAccess) {
|
||||
const requestWithPortHash =
|
||||
request.url === "elm-pages-internal://port"
|
||||
? { portsHash, ...request }
|
||||
: request;
|
||||
if (hasFsAccess) {
|
||||
return path.join(
|
||||
process.cwd(),
|
||||
".elm-pages",
|
||||
"http-response-cache",
|
||||
requestToString(request)
|
||||
requestToString(requestWithPortHash)
|
||||
);
|
||||
} else {
|
||||
return path.join("/", requestToString(request));
|
||||
return path.join("/", requestToString(requestWithPortHash));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} mode
|
||||
* @param {{url: string; headers: {[x: string]: string}; method: string; body: Body } } rawRequest
|
||||
* @param {{url: string;headers: {[x: string]: string;};method: string;body: Body;}} rawRequest
|
||||
* @returns {Promise<string>}
|
||||
* @param {string} portsFile
|
||||
* @param {boolean} hasFsAccess
|
||||
*/
|
||||
function lookupOrPerform(mode, rawRequest, hasFsAccess) {
|
||||
function lookupOrPerform(portsFile, mode, rawRequest, hasFsAccess) {
|
||||
const { fs } = require("./request-cache-fs.js")(hasFsAccess);
|
||||
return new Promise(async (resolve, reject) => {
|
||||
const request = toRequest(rawRequest);
|
||||
const responsePath = fullPath(request, hasFsAccess);
|
||||
const portsHash = portsFile.match(/-([^-]+)\.mjs$/)[1];
|
||||
const responsePath = fullPath(portsHash, request, hasFsAccess);
|
||||
|
||||
// TODO check cache expiration time and delete and go to else if expired
|
||||
if (await checkFileExists(fs, responsePath)) {
|
||||
@ -48,17 +55,14 @@ function lookupOrPerform(mode, rawRequest, hasFsAccess) {
|
||||
let portDataSource = {};
|
||||
let portDataSourceFound = false;
|
||||
try {
|
||||
portDataSource = requireUncached(
|
||||
mode,
|
||||
path.join(process.cwd(), "port-data-source.js")
|
||||
);
|
||||
portDataSource = await import(path.join(process.cwd(), portsFile));
|
||||
portDataSourceFound = true;
|
||||
} catch (e) {}
|
||||
|
||||
if (request.url.startsWith("port://")) {
|
||||
if (request.url === "elm-pages-internal://port") {
|
||||
try {
|
||||
const portName = request.url.replace(/^port:\/\//, "");
|
||||
// console.time(JSON.stringify(request.url));
|
||||
const { input, portName } = rawRequest.body.args[0];
|
||||
|
||||
if (!portDataSource[portName]) {
|
||||
if (portDataSourceFound) {
|
||||
throw `DataSource.Port.send "${portName}" is not defined. Be sure to export a function with that name from port-data-source.js`;
|
||||
@ -70,9 +74,7 @@ function lookupOrPerform(mode, rawRequest, hasFsAccess) {
|
||||
}
|
||||
await fs.promises.writeFile(
|
||||
responsePath,
|
||||
JSON.stringify(
|
||||
await portDataSource[portName](rawRequest.body.args[0])
|
||||
)
|
||||
JSON.stringify(jsonResponse(await portDataSource[portName](input)))
|
||||
);
|
||||
resolve(responsePath);
|
||||
} catch (error) {
|
||||
@ -228,4 +230,11 @@ function requireUncached(mode, filePath) {
|
||||
return require(filePath);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {unknown} json
|
||||
*/
|
||||
function jsonResponse(json) {
|
||||
return { bodyKind: "json", body: json };
|
||||
}
|
||||
|
||||
module.exports = { lookupOrPerform };
|
||||
|
1
package-lock.json
generated
1
package-lock.json
generated
@ -19,6 +19,7 @@
|
||||
"devcert": "^1.2.0",
|
||||
"elm-doc-preview": "^5.0.5",
|
||||
"elm-hot": "^1.1.6",
|
||||
"esbuild": "^0.14.23",
|
||||
"fs-extra": "^10.0.0",
|
||||
"globby": "11.0.4",
|
||||
"gray-matter": "^4.0.3",
|
||||
|
@ -33,6 +33,7 @@
|
||||
"devcert": "^1.2.0",
|
||||
"elm-doc-preview": "^5.0.5",
|
||||
"elm-hot": "^1.1.6",
|
||||
"esbuild": "^0.14.23",
|
||||
"fs-extra": "^10.0.0",
|
||||
"globby": "11.0.4",
|
||||
"gray-matter": "^4.0.3",
|
||||
@ -73,4 +74,4 @@
|
||||
"bin": {
|
||||
"elm-pages": "generator/src/cli.js"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -8,8 +8,9 @@ module DataSource.Port exposing (get)
|
||||
|
||||
import DataSource
|
||||
import DataSource.Http
|
||||
import DataSource.Internal.Request
|
||||
import Json.Decode exposing (Decoder)
|
||||
import Json.Encode
|
||||
import Json.Encode as Encode
|
||||
|
||||
|
||||
{-| In a vanilla Elm application, ports let you either send or receive JSON data between your Elm application and the JavaScript context in the user's browser at runtime.
|
||||
@ -73,12 +74,17 @@ prefer to add ANSI color codes within the error string in an exception and it wi
|
||||
As with any JavaScript or NodeJS code, avoid doing blocking IO operations. For example, avoid using `fs.readFileSync`, because blocking IO can slow down your elm-pages builds and dev server.
|
||||
|
||||
-}
|
||||
get : String -> Json.Encode.Value -> Decoder b -> DataSource.DataSource b
|
||||
get : String -> Encode.Value -> Decoder b -> DataSource.DataSource b
|
||||
get portName input decoder =
|
||||
DataSource.Http.request
|
||||
{ url = "port://" ++ portName
|
||||
, method = "GET"
|
||||
, headers = []
|
||||
, body = DataSource.Http.jsonBody input
|
||||
DataSource.Internal.Request.request
|
||||
{ name = "port"
|
||||
, body =
|
||||
Encode.object
|
||||
[ ( "input", input )
|
||||
, ( "portName", Encode.string portName )
|
||||
]
|
||||
|> DataSource.Http.jsonBody
|
||||
, expect =
|
||||
decoder
|
||||
|> DataSource.Http.expectJson
|
||||
}
|
||||
(DataSource.Http.expectJson decoder)
|
||||
|
Loading…
Reference in New Issue
Block a user