Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Compile with ESM and a ServiceWorker #477

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .npmrc
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
registry = https://registry.npmjs.org
ignore-scripts = false
ignore-scripts = true


public-hoist-pattern[]=*@types*
Expand Down
2 changes: 0 additions & 2 deletions frontend/app/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@
<meta name="description" content="Limber is a Glimdown playground for demonstrating how Glimmer and Markdown can be used together to write documentation">
<meta name="viewport" content="width=device-width, initial-scale=1">

<script src='https://js.sentry-cdn.com/cbe554dff4374619833a5dcbdaf7407c.min.js' crossorigin="anonymous"></script>

{{content-for "head"}}

<link integrity="" rel="stylesheet" href="{{rootURL}}assets/tailwind.css">
Expand Down
26 changes: 13 additions & 13 deletions frontend/app/services/-compile/babel/index.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
import { compileJS as cjs } from 'ember-repl';
// import config from 'limber/config/environment';

// import { compile as worker } from './worker';
import { compile as worker } from './worker';

// import type { ExtractedCode } from '../markdown-to-ember';
// import type { AsyncReturnType } from 'type-fest';
import type { AsyncReturnType } from 'type-fest';
import { ExtractedCode } from '../markdown-to-ember';

// type CompiledViaWorker = AsyncReturnType<typeof worker>;
// type CompiledViaCJS = AsyncReturnType<typeof cjs>;
// export type CompileOutput = CompiledViaCJS[0] | CompiledViaWorker[0];
type CompiledViaWorker = AsyncReturnType<typeof worker>;
type CompiledViaCJS = AsyncReturnType<typeof cjs>;
export type CompileOutput = CompiledViaCJS | CompiledViaWorker;

// export async function compileJS(_id: string, js: ExtractedCode[]): Promise<CompileOutput[]> {
// if (config.SERVICE_WORKER) {
// return worker(js);
// }
export async function compileJS(info: ExtractedCode): Promise<CompileOutput> {
// if (config.SERVICE_WORKER) {
return worker(info);
// }

// // use compileAll from -compile/index
// // return cjs(js);
// }
// return cjs(js);
}
124 changes: 79 additions & 45 deletions frontend/app/services/-compile/babel/worker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,74 +2,82 @@ import type { ExtractedCode } from '../markdown-to-ember';

let isRegistered = false;

export async function compile(js: ExtractedCode[]) {
let moduleInfos = await compileModules(js);

let missing = moduleInfos.filter(({ importPath }) => !importPath);

if (missing.length > 0) {
let first = missing[0];

throw new Error(`Component, ${first.name}, failed to compile`);
class CompileError extends Error {
constructor(
public message: string,
public response: Response,
public code: string,
public url: string
) {
super(message);
}

return moduleInfos;
}

export async function compileModules(js: ExtractedCode[]) {
export async function compile(js: ExtractedCode) {
if (!isRegistered) {
await installImportMap();
// await installImportMap();
await setupServiceWorker();
await establishRequireJSEntries();

isRegistered = true;
}

let responses = await Promise.all(
js.map(async ({ name, code }) => {
let qps = new URLSearchParams();
let { name, code } = js;

let qps = new URLSearchParams();

qps.set('n', name);
qps.set('q', code);

qps.set('n', name);
qps.set('q', code);
let url = `/compile-sw?${qps}`;

let response = await fetch(`/compile-sw?${qps}`);
let { importPath } = await response.json();
let response = await fetch(url, {
headers: { 'Content-Type': 'application/json' },
});

return { name, importPath };
})
);
if (response.status !== 200) {
throw new CompileError(
`${response.status} | Could not compile code. See console for details.`,
response,
code,
url
);
}

let { importPath } = await response.json();

return responses;
return { name, importPath };
}

async function installImportMap() {
let script = document.createElement('script');
// async function installImportMap() {
// let script = document.createElement('script');

script.setAttribute('type', 'importmap');
// script.setAttribute('type', 'importmap');

// let response = await import(
// /* webpackIgnore: true */
// 'https://raw.githubusercontent.com/ef4/mho/a4391e53891f3f6321f0a8f36de88ec23511dbee/ember-app/importmap.json'
// );
// External Import maps are not supported yet
// let response = await fetch(
// 'https://raw.githubusercontent.com/ef4/mho/a4391e53891f3f6321f0a8f36de88ec23511dbee/ember-app/importmap.json'
// );
// let importmap = await response.text();
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
let importmap = (await import('/mho-importmap.json')).default;
// // let response = await import(
// // /* webpackIgnore: true */
// // 'https://raw.githubusercontent.com/ef4/mho/a4391e53891f3f6321f0a8f36de88ec23511dbee/ember-app/importmap.json'
// // );
// // External Import maps are not supported yet
// // let response = await fetch(
// // 'https://raw.githubusercontent.com/ef4/mho/a4391e53891f3f6321f0a8f36de88ec23511dbee/ember-app/importmap.json'
// // );
// // let importmap = await response.text();
// // eslint-disable-next-line @typescript-eslint/ban-ts-comment
// // @ts-ignore
// let importmap = (await import('/mho-importmap.json')).default;

console.debug({ importmap });
// console.debug({ importmap });

script.innerHTML = JSON.stringify(importmap);
document.body.appendChild(script);
}
// script.innerHTML = JSON.stringify(importmap);
// document.body.appendChild(script);
// }

async function setupServiceWorker() {
if ('serviceWorker' in navigator) {
let registration = await navigator.serviceWorker.register('/transpilation-worker.js');
let registration = await navigator.serviceWorker.register('/transpile.js');

// registration.update();
registration.update();

console.info('ServiceWorker registration successful with scope: ', registration.scope);

Expand All @@ -82,8 +90,34 @@ async function setupServiceWorker() {
}, 50);
});

console.info('ServiceWorker activated.');

return registration;
}

throw new Error(`ServiceWorker is required`);
}

async function establishRequireJSEntries() {
await fetch('/populate-sw', {
method: 'POST',
// Require JS is private API, but we need to swap out imports in the code
// snippets for accessing the same requirejs modules that are in this app.
//
// This is to
// - reduce overall shipped JS
//
// However, if we were to make the rendering area an entirely different app,
// we could then isolate compile errors and not have fatal problems that require
// a browser page refresh -- this may be the best thing to do in the long term.
//
// But for now, we need to at least try the requirejs stuff, because it's a requirement
// for design-system REPLs
//
// eslint-disable-next-line @typescript-eslint/no-explicit-any
body: JSON.stringify(Object.keys((window.requirejs as any).entries)),
headers: {
'Content-Type': 'application/json',
},
});
}
26 changes: 12 additions & 14 deletions frontend/app/services/-compile/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
/* eslint-disable @typescript-eslint/ban-types */
import { compileHBS, compileJS, invocationName } from 'ember-repl';
import { compileHBS, invocationName } from 'ember-repl';
import { compileJS } from './babel';
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-expect-error
import CopyMenu from 'limber/components/limber/copy-menu';
Expand All @@ -19,14 +20,14 @@ interface CompilationResult {
errorLine?: number;
}

export async function compileAll(js: { code: string }[]) {
export async function compileAll(js: ExtractedCode[]) {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
let { COMPONENT_MAP } = await import('/ember-repl/component-map.js');

let modules = await Promise.all(
js.map(async ({ code }) => {
return await compileJS(code, COMPONENT_MAP);
js.map(async (info) => {
return await compileJS(info);
})
);

Expand Down Expand Up @@ -71,13 +72,13 @@ export async function compile(glimdownInput: string): Promise<CompilationResult>
compiled.map(async (info) => {
// using web worker + import maps is not available yet (need firefox support)
// (and to somehow be able to point at npm)
//
// if ('importPath' in info) {
// return scope.push({
// moduleName: name,
// component: await import(/* webpackIgnore: true */ info.importPath),
// });
// }

if ('importPath' in info) {
return scope.push({
moduleName: name,
component: await import(/* webpackIgnore: true */ info.importPath),
});
}

return scope.push(info);
})
Expand All @@ -88,9 +89,6 @@ export async function compile(glimdownInput: string): Promise<CompilationResult>
scope.push(compileHBS(code));
}
} catch (error) {
console.info({ scope });
console.error(error);

return { error, rootTemplate };
}
}
Expand Down
14 changes: 14 additions & 0 deletions frontend/ember-cli-build.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,18 @@ module.exports = function (defaults) {
`);

let config = {
contentFor(_, __, type) {
if (type === 'head' && isProduction) {
return `
<script
src='https://js.sentry-cdn.com/cbe554dff4374619833a5dcbdaf7407c.min.js'
crossorigin="anonymous">
</script>
`
}

this._super.call(this, ...arguments);
},
'ember-cli-terser': {
enabled: MINIFY,
},
Expand All @@ -44,6 +56,8 @@ module.exports = function (defaults) {

return require('@embroider/compat').compatBuild(app, Webpack, {
extraPublicTrees: [
// Service Worker / transpilation
require('@nullvoxpopuli/limber-transpilation/broccoli-funnel')(),
// Mobile Editor
require('@nullvoxpopuli/limber-codemirror/broccoli-funnel')(),
// Desktop Editor
Expand Down
18 changes: 18 additions & 0 deletions packages/transpilation/broccoli-funnel.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
'use strict';

const path = require('path');
const Funnel = require('broccoli-funnel');

const SRC_FILES = path.join(__dirname, 'dist');

/**
* This broccoli funnel is for copying the built assets to a target
* app's public folder. No building occurs
*
*/
module.exports = function distWatcher() {
return new Funnel(SRC_FILES, {
// dist or whatever the root of the output directory is
destDir: '/',
});
};
Loading