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 busboy = require("busboy");
|
||||||
const { createServer: createViteServer } = require("vite");
|
const { createServer: createViteServer } = require("vite");
|
||||||
const cliVersion = require("../../package.json").version;
|
const cliVersion = require("../../package.json").version;
|
||||||
|
const esbuild = require("esbuild");
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {{ port: string; base: string; https: boolean; debug: boolean; }} options
|
* @param {{ port: string; base: string; https: boolean; debug: boolean; }} options
|
||||||
@ -137,6 +138,40 @@ async function start(options) {
|
|||||||
base: options.base,
|
base: options.base,
|
||||||
...viteConfig,
|
...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()
|
const app = connect()
|
||||||
.use(timeMiddleware())
|
.use(timeMiddleware())
|
||||||
@ -288,6 +323,7 @@ async function start(options) {
|
|||||||
mode: "dev-server",
|
mode: "dev-server",
|
||||||
pathname,
|
pathname,
|
||||||
serverRequest,
|
serverRequest,
|
||||||
|
portsFilePath: global.portsFilePath,
|
||||||
});
|
});
|
||||||
readyThread.worker.on("message", (message) => {
|
readyThread.worker.on("message", (message) => {
|
||||||
if (message.tag === "done") {
|
if (message.tag === "done") {
|
||||||
@ -379,6 +415,7 @@ async function start(options) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
req.on("end", async function () {
|
req.on("end", async function () {
|
||||||
|
// TODO run render directly instead of in worker thread
|
||||||
await runRenderThread(
|
await runRenderThread(
|
||||||
await reqToJson(req, body, requestTime),
|
await reqToJson(req, body, requestTime),
|
||||||
pathname,
|
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 = {};
|
global.staticHttpCache = {};
|
||||||
|
|
||||||
async function run({ mode, pathname, serverRequest }) {
|
async function run({ mode, pathname, serverRequest, portsFilePath }) {
|
||||||
console.time(`${threadId} ${pathname}`);
|
console.time(`${threadId} ${pathname}`);
|
||||||
try {
|
try {
|
||||||
const renderResult = await renderer(
|
const renderResult = await renderer(
|
||||||
|
portsFilePath,
|
||||||
workerData.basePath,
|
workerData.basePath,
|
||||||
requireElm(mode),
|
requireElm(mode),
|
||||||
mode,
|
mode,
|
||||||
|
@ -30,6 +30,7 @@ module.exports =
|
|||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
async function run(
|
async function run(
|
||||||
|
portsFile,
|
||||||
basePath,
|
basePath,
|
||||||
elmModule,
|
elmModule,
|
||||||
mode,
|
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)
|
// we can provide a fake HTTP instead of xhr2 (which is otherwise needed for Elm HTTP requests from Node)
|
||||||
XMLHttpRequest = {};
|
XMLHttpRequest = {};
|
||||||
const result = await runElmApp(
|
const result = await runElmApp(
|
||||||
|
portsFile,
|
||||||
basePath,
|
basePath,
|
||||||
elmModule,
|
elmModule,
|
||||||
mode,
|
mode,
|
||||||
@ -71,6 +73,7 @@ module.exports =
|
|||||||
* @returns {Promise<({is404: boolean} & ( { kind: 'json'; contentJson: string} | { kind: 'html'; htmlString: string } | { kind: 'api-response'; body: string; }) )>}
|
* @returns {Promise<({is404: boolean} & ( { kind: 'json'; contentJson: string} | { kind: 'html'; htmlString: string } | { kind: 'api-response'; body: string; }) )>}
|
||||||
*/
|
*/
|
||||||
function runElmApp(
|
function runElmApp(
|
||||||
|
portsFile,
|
||||||
basePath,
|
basePath,
|
||||||
elmModule,
|
elmModule,
|
||||||
mode,
|
mode,
|
||||||
@ -161,7 +164,10 @@ function runElmApp(
|
|||||||
}
|
}
|
||||||
} else if (fromElm.tag === "DoHttp") {
|
} else if (fromElm.tag === "DoHttp") {
|
||||||
const requestToPerform = fromElm.args[0];
|
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(
|
runInternalJob(
|
||||||
app,
|
app,
|
||||||
mode,
|
mode,
|
||||||
@ -171,7 +177,7 @@ function runElmApp(
|
|||||||
patternsToWatch
|
patternsToWatch
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
runHttpJob(app, mode, requestToPerform, fs, hasFsAccess);
|
runHttpJob(portsFile, app, mode, requestToPerform, fs, hasFsAccess);
|
||||||
}
|
}
|
||||||
} else if (fromElm.tag === "Errors") {
|
} else if (fromElm.tag === "Errors") {
|
||||||
foundErrors = true;
|
foundErrors = true;
|
||||||
@ -229,10 +235,18 @@ async function outputString(
|
|||||||
|
|
||||||
/** @typedef { { head: any[]; errors: any[]; contentJson: any[]; html: string; route: string; title: string; } } Arg */
|
/** @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;
|
pendingDataSourceCount += 1;
|
||||||
try {
|
try {
|
||||||
const responseFilePath = await lookupOrPerform(
|
const responseFilePath = await lookupOrPerform(
|
||||||
|
portsFile,
|
||||||
mode,
|
mode,
|
||||||
requestToPerform,
|
requestToPerform,
|
||||||
hasFsAccess
|
hasFsAccess
|
||||||
|
@ -16,29 +16,36 @@ function requestToString(request) {
|
|||||||
/**
|
/**
|
||||||
* @param {Object} 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) {
|
if (hasFsAccess) {
|
||||||
return path.join(
|
return path.join(
|
||||||
process.cwd(),
|
process.cwd(),
|
||||||
".elm-pages",
|
".elm-pages",
|
||||||
"http-response-cache",
|
"http-response-cache",
|
||||||
requestToString(request)
|
requestToString(requestWithPortHash)
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
return path.join("/", requestToString(request));
|
return path.join("/", requestToString(requestWithPortHash));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {string} mode
|
* @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>}
|
* @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);
|
const { fs } = require("./request-cache-fs.js")(hasFsAccess);
|
||||||
return new Promise(async (resolve, reject) => {
|
return new Promise(async (resolve, reject) => {
|
||||||
const request = toRequest(rawRequest);
|
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
|
// TODO check cache expiration time and delete and go to else if expired
|
||||||
if (await checkFileExists(fs, responsePath)) {
|
if (await checkFileExists(fs, responsePath)) {
|
||||||
@ -48,17 +55,14 @@ function lookupOrPerform(mode, rawRequest, hasFsAccess) {
|
|||||||
let portDataSource = {};
|
let portDataSource = {};
|
||||||
let portDataSourceFound = false;
|
let portDataSourceFound = false;
|
||||||
try {
|
try {
|
||||||
portDataSource = requireUncached(
|
portDataSource = await import(path.join(process.cwd(), portsFile));
|
||||||
mode,
|
|
||||||
path.join(process.cwd(), "port-data-source.js")
|
|
||||||
);
|
|
||||||
portDataSourceFound = true;
|
portDataSourceFound = true;
|
||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
|
|
||||||
if (request.url.startsWith("port://")) {
|
if (request.url === "elm-pages-internal://port") {
|
||||||
try {
|
try {
|
||||||
const portName = request.url.replace(/^port:\/\//, "");
|
const { input, portName } = rawRequest.body.args[0];
|
||||||
// console.time(JSON.stringify(request.url));
|
|
||||||
if (!portDataSource[portName]) {
|
if (!portDataSource[portName]) {
|
||||||
if (portDataSourceFound) {
|
if (portDataSourceFound) {
|
||||||
throw `DataSource.Port.send "${portName}" is not defined. Be sure to export a function with that name from port-data-source.js`;
|
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(
|
await fs.promises.writeFile(
|
||||||
responsePath,
|
responsePath,
|
||||||
JSON.stringify(
|
JSON.stringify(jsonResponse(await portDataSource[portName](input)))
|
||||||
await portDataSource[portName](rawRequest.body.args[0])
|
|
||||||
)
|
|
||||||
);
|
);
|
||||||
resolve(responsePath);
|
resolve(responsePath);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@ -228,4 +230,11 @@ function requireUncached(mode, filePath) {
|
|||||||
return require(filePath);
|
return require(filePath);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {unknown} json
|
||||||
|
*/
|
||||||
|
function jsonResponse(json) {
|
||||||
|
return { bodyKind: "json", body: json };
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = { lookupOrPerform };
|
module.exports = { lookupOrPerform };
|
||||||
|
1
package-lock.json
generated
1
package-lock.json
generated
@ -19,6 +19,7 @@
|
|||||||
"devcert": "^1.2.0",
|
"devcert": "^1.2.0",
|
||||||
"elm-doc-preview": "^5.0.5",
|
"elm-doc-preview": "^5.0.5",
|
||||||
"elm-hot": "^1.1.6",
|
"elm-hot": "^1.1.6",
|
||||||
|
"esbuild": "^0.14.23",
|
||||||
"fs-extra": "^10.0.0",
|
"fs-extra": "^10.0.0",
|
||||||
"globby": "11.0.4",
|
"globby": "11.0.4",
|
||||||
"gray-matter": "^4.0.3",
|
"gray-matter": "^4.0.3",
|
||||||
|
@ -33,6 +33,7 @@
|
|||||||
"devcert": "^1.2.0",
|
"devcert": "^1.2.0",
|
||||||
"elm-doc-preview": "^5.0.5",
|
"elm-doc-preview": "^5.0.5",
|
||||||
"elm-hot": "^1.1.6",
|
"elm-hot": "^1.1.6",
|
||||||
|
"esbuild": "^0.14.23",
|
||||||
"fs-extra": "^10.0.0",
|
"fs-extra": "^10.0.0",
|
||||||
"globby": "11.0.4",
|
"globby": "11.0.4",
|
||||||
"gray-matter": "^4.0.3",
|
"gray-matter": "^4.0.3",
|
||||||
@ -73,4 +74,4 @@
|
|||||||
"bin": {
|
"bin": {
|
||||||
"elm-pages": "generator/src/cli.js"
|
"elm-pages": "generator/src/cli.js"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,8 +8,9 @@ module DataSource.Port exposing (get)
|
|||||||
|
|
||||||
import DataSource
|
import DataSource
|
||||||
import DataSource.Http
|
import DataSource.Http
|
||||||
|
import DataSource.Internal.Request
|
||||||
import Json.Decode exposing (Decoder)
|
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.
|
{-| 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.
|
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 =
|
get portName input decoder =
|
||||||
DataSource.Http.request
|
DataSource.Internal.Request.request
|
||||||
{ url = "port://" ++ portName
|
{ name = "port"
|
||||||
, method = "GET"
|
, body =
|
||||||
, headers = []
|
Encode.object
|
||||||
, body = DataSource.Http.jsonBody input
|
[ ( "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