diff --git a/.gitignore b/.gitignore index 4ff62df..5aa9204 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ .DS_Store **/*.log +/dist /node_modules /packages/builder-worker/test/test-app /packages/builder-worker/.file-daemon-key @@ -36,4 +37,4 @@ /packages/shared/**/*.js.map !webpack.config.js -!/cdn/**/dist + diff --git a/.vscode/launch.json b/.vscode/launch.json index 4c974db..7000262 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -12,7 +12,23 @@ "env": {}, "console": "integratedTerminal", "program": "${workspaceRoot}/dist/file-daemon/main.js", - "args": [".", "../test-lib", "../recipes", "../../cdn/pkgs"] + "args": [".", "../test-lib"] + }, + { + "type": "node", + "request": "launch", + "name": "Serve ember-app", + "cwd": "${workspaceRoot}/packages/ember-app", + "env": {}, + "console": "integratedTerminal", + "program": "${workspaceRoot}/packages/file-daemon/bin/file-daemon.js", + "args": [ + "--builderServer=http://localhost:8080", + "--uiServer=http://localhost:4300/catalogjs/ui/", + "--pkgsPath=../../working/cdn/pkgs", + "--ignore=node_modules", + "/private/var/folders/8z/_x2_6pv12n96tlr8g19hqp6h0000gn/T/embroider/e08385/packages/ember-app" + ] }, { "type": "node", @@ -24,10 +40,9 @@ "args": [ "--builderServer=http://localhost:8080", "--uiServer=http://localhost:4300/catalogjs/ui/", + "--pkgsPath=../../working/cdn/pkgs", ".", - "../test-lib", - "../recipes", - "../../cdn/pkgs" + "../recipes" ] }, { @@ -40,9 +55,9 @@ "args": [ "--builderServer=http://localhost:8080", "--uiServer=http://localhost:4300/catalogjs/ui/", + "--pkgsPath=../../working/cdn/pkgs", ".", - "../recipes", - "../../cdn/pkgs" + "../recipes" ] }, { @@ -99,6 +114,20 @@ "program": "${workspaceRoot}/packages/builder-node/bin/install.js", "args": ["--project=.", "@babel/core"] }, + { + "type": "node", + "request": "launch", + "name": "Run ember app pkg install", + "cwd": "${workspaceRoot}", + "console": "integratedTerminal", + "env": {}, + "program": "${workspaceRoot}/packages/builder-node/bin/install.js", + "args": [ + // "--project=/private/var/folders/8z/_x2_6pv12n96tlr8g19hqp6h0000gn/T/embroider/e08385/packages/ember-app", + "--project=/private/var/folders/8z/_x2_6pv12n96tlr8g19hqp6h0000gn/T/embroider/e08385", + "ember-welcome-page/components/welcome-page" + ] + }, { "type": "node", "request": "launch", diff --git a/README.md b/README.md index ecd85d0..d2c075c 100644 --- a/README.md +++ b/README.md @@ -26,12 +26,16 @@ Note: if you want to work with a local CatalogJS package registry, then you can ## Building it -Setup all the npm dependencies: +1. Setup all the npm dependencies: ```sh yarn install ``` +2. Yarn link the `@embroider/core` and `@embroider/compat` packages from the `catalogjs-experiment` branch of the https://github.com/ef4/snowpack-experiment repo to the `packages/ember-app` workspace in this repository. (This step eventually goes away after some key upstream embroider changes are made available). + +3. Update the `packages/src/core/babel-plugin-adjust-imports.ts#adjustSpecifier` method to indicate if the build is using the file daemon or not (`runningInsideFileDaemon` variable), and recompile the TypeScript + ## Running it (for purposes of development) 1. Perform typescript compilation: @@ -49,12 +53,22 @@ yarn install ``` 4. Start serving the sample app: ```sh - yarn serve + yarn serve:test-app + ``` + or optionally serving the sample ember-app with hosted pkgs: + ```sh + yarn serve:ember-app + ``` + or optionally serving the sample ember-app with local pkgs: + ```sh + yarn serve:ember-app-local-pkgs ``` + This will run both an ember-cli server which uses embroider as scaffolding for generating stage 2 outputs, as well as running a file daemon to host those stage2 outputs. + Note: if you want to be able to work with recipes locally (instead of being served from github, which is the default), then you can additionally mount the `../recipes` folder in the package.json for the `packages/test-app` (or `packages/test-lib`). When a local recipes filesystem is mounted we will use that instead of the github hosted recipes. - Note: if you want to be able to work with a local CatalogJS package registry, then you can provide the `--pkgsPath path/to/your/pkgs` parameter to start script command in the package.json for the `packages/test-app` (or `packages/test-lib`). Make sure to use the `packages/builder-node/bin/install.js` script to generate your CatalogJS packages locally. + Note: if you want to be able to work with a local CatalogJS package registry, then you can provide the `--pkgsPath path/to/your/pkgs` parameter to start script command in the package.json for the `packages/test-app` (or `packages/test-lib`). Make sure to use the `packages/builder-node/bin/install.js` script to generate your CatalogJS packages locally. As a convenience, all the test apps have a yarn script to serve the packages locally from the `./working/cdn/pkgs` folder. For example, in `packages/test-app/package.json` there is a `start:local-pkgs` script that is available to use. If you want to use a `tsc --watch`, it's a little tricky since each package has its own `tsconfig.json` and needs to have it's own `tsc --watch`. You can run @@ -70,6 +84,9 @@ yarn pkg ``` And the package will be created in the `./dist` folder. From there the `./dist/catalogjs` script can be executed. +### Generating bundles for ember-app +To generate the ember-app bundles, there is a yarn script, `yarn ember-acceptance-test` that will generate the bundles for the ember-app. eventually this will be the full acceptance tests for the ember app after the next phase of work is completed. Make sure the hacked embroider is set to `runningInsideFileDaemon = false` and TS compiled before making the bundles. + ## Testing it From the command line you can run all the tests by executing the following from @@ -94,16 +111,12 @@ running in your browser) that the CLI is using and will troll you. To run the node tests: -1. cd to `packages/builder-worker` -2. run webpack: - ```sh - yarn webpack - ``` - (use the `--watch` option if you want to watch the file system for changes) -3. run the tests: - ```sh - yarn qunit assets/node-test.js - ``` +1. cd to `packages/builder-node` +2. run `yarn test` +3. cd to `packages/file-daemon` +4. run `yarn test` +5. cd to `packages/file-daemon-client` +7. run `yarn test` ## Releasing it diff --git a/package.json b/package.json index 16abd20..4ef995d 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,8 @@ "**/@types/qunit", "**/@types/rsvp", "**/ember-load-initializers", - "**/ember-load-initializers/**" + "**/ember-load-initializers/**", + "**/@ember/string" ] }, "private": true, @@ -40,7 +41,7 @@ "test:file-daemon": "cd packages/file-daemon && yarn test", "test:file-daemon-client": "cd packages/file-daemon-client && yarn test", "test:builder-worker": "cd packages/builder-worker && yarn test", - "test:builder-node": "cd packages/builder-worker && yarn webpack && yarn qunit assets/node-test.js", + "test:builder-node": "cd packages/builder-node && yarn test", "acceptance-test": "npm-run-all acceptance-test:*", "acceptance-test:init": "rm -rf ./test-working && mkdir ./test-working", "acceptance-test:bundle": "cd ./test-working && node ../packages/builder-node/bin/install.js --project=.. @babel/core", @@ -61,11 +62,23 @@ "publish-pkgs": "aws s3 cp ./working/cdn/pkgs s3://catalogjs-pkgs --recursive", "release": "yarn pkg && yarn pub", "builder": "cd packages/builder-worker && yarn start", - "serve": "cd packages/test-app && yarn start", - "ui": "cd packages/ui && yarn start" + "serve:test-app": "cd packages/test-app && yarn start", + "serve:test-lib": "cd packages/test-lib && yarn start", + "serve:ember-app": "yarn start-server-and-test serve:embroider 'http://localhost:4250/.stage2-output' serve:file-daemon-ember-app", + "serve:ember-app-local-pkgs": "yarn start-server-and-test serve:embroider 'http://localhost:4250/.stage2-output' serve:file-daemon-ember-app-local-pkgs", + "serve:embroider": "cd packages/ember-app && yarn start", + "serve:file-daemon-ember-app": "cd packages/ember-app && yarn start:catalogjs", + "serve:file-daemon-ember-app-local-pkgs": "cd packages/ember-app && yarn start:catalogjs-local-pkgs", + "ui": "cd packages/ui && yarn start", + "ember-acceptance-test": "npm-run-all ember-acceptance-test:*", + "ember-acceptance-test:build": "cd packages/ember-app && yarn build", + "ember-acceptance-test:bundle-runtime": "node packages/builder-node/bin/install.js --project=. @babel/runtime/helpers/esm/defineProperty", + "ember-acceptance-test:bundle-deps": "node packages/builder-node/bin/install.js --project=`cat ./packages/ember-app/dist/.stage2-output` ember-resolver ember-load-initializers @glimmer/component ember-cli-app-version/initializer-factory", + "ember-acceptance-test:bundle-addons": "node packages/builder-node/bin/install.js --project=`cat ./packages/ember-app/dist/.stage2-output`/../.. ember-page-title/helpers/page-title ember-welcome-page/components/welcome-page" }, "devDependencies": { "npm-run-all": "^4.1.5", + "start-server-and-test": "^1.11.0", "testem": "^3.1.0" }, "resolutions": { diff --git a/packages/builder-dom/main.ts b/packages/builder-dom/main.ts index f9c991a..f82a300 100644 --- a/packages/builder-dom/main.ts +++ b/packages/builder-dom/main.ts @@ -8,75 +8,78 @@ import { isReloadEvent } from "../builder-worker/src/client-reload"; let uiWidth: number; -// Note that the packages/ui ember app is responsible for the service worker -// initialization and activation. At this point the service worker has already -// been activated. The responsibility for this module is to render the ui's edge -// route in an iframe, add a build reload-page listener, and respond to window positioning. requests. - -if (navigator.serviceWorker.controller) { - let iframe = document.createElement("iframe"); - iframe.setAttribute("src", `${window.origin}/catalogjs/ui/#/edge`); - iframe.setAttribute("id", "catalogjs-ui"); - iframe.setAttribute( - "style", - `position: fixed; - top: 0; - bottom: 0; - right: -100%; - height: 100vh; - border: none; - transition: transform 300ms; - ` - ); - if (!document.body) { - document.body = document.createElement("body"); +function handleUICommand(event: MessageEvent) { + if (event.origin !== window.origin) { + return; + } + let { data } = event; + if (isReloadEvent(data)) { + window.location.reload(); + return; } - document.body.setAttribute("style", "overflow-x: hidden;"); - document.body.append(iframe); - window.addEventListener("message", handleUICommand, false); - navigator.serviceWorker.addEventListener("message", handleUICommand); - - (async () => { - await fetch("/register-client/reload"); - })(); - function handleUICommand(event: MessageEvent) { - if (event.origin !== window.origin) { - return; - } - let { data } = event; - if (isReloadEvent(data)) { - window.location.reload(); - return; + if (isUIManagerEvent(data)) { + let iframe = document.getElementById("catalogjs-ui"); + if (!iframe) { + throw new Error("bug: cannot find the catalogjs ui iframe"); } - if (isUIManagerEvent(data)) { - let iframe = document.getElementById("catalogjs-ui"); - if (!iframe) { - throw new Error("bug: cannot find the catalogjs ui iframe"); + let { uiManager: command } = data; + if (command) { + switch (command.type) { + case "ready": + uiWidth = command.width; + iframe.style.width = `${command.width}px`; + iframe.style.right = `${-1 * command.width}px`; + break; + case "show": + iframe.style.transform = `translate(${-1 * uiWidth}px, 0)`; + break; + case "hide": + iframe.style.transform = `translate(0, 0)`; + break; + case "home": + location.href = "/catalogjs/ui/"; + break; + default: + assertNever(command); } + } + } +} - let { uiManager: command } = data; - if (command) { - switch (command.type) { - case "ready": - uiWidth = command.width; - iframe.style.width = `${command.width}px`; - iframe.style.right = `${-1 * command.width}px`; - break; - case "show": - iframe.style.transform = `translate(${-1 * uiWidth}px, 0)`; - break; - case "hide": - iframe.style.transform = `translate(0, 0)`; - break; - case "home": - location.href = "/catalogjs/ui/"; - break; - default: - assertNever(command); - } - } +// Note that the packages/ui ember app is responsible for the service worker +// initialization and activation. At this point the service worker has already +// been activated. The responsibility for this module is to render the ui's edge +// route in an iframe, add a build reload-page listener, and respond to window +// positioning requests. + +if (navigator.serviceWorker.controller) { + if (!document.getElementById("catalogjs-ui")) { + window.addEventListener("message", handleUICommand, false); + navigator.serviceWorker.addEventListener("message", handleUICommand); + let iframe = document.createElement("iframe"); + iframe.setAttribute("src", `${window.origin}/catalogjs/ui/#/edge`); + iframe.setAttribute("id", "catalogjs-ui"); + iframe.setAttribute( + "style", + `position: fixed; + top: 0; + bottom: 0; + right: -100%; + height: 100vh; + border: none; + transition: transform 300ms; + ` + ); + if (!document.body) { + document.body = document.createElement("body"); } + document.body.setAttribute("style", "overflow-x: hidden;"); + document.body.append(iframe); + + (async () => { + await fetch("/register-client/reload"); + })(); } } diff --git a/packages/builder-node/package.json b/packages/builder-node/package.json index d1a0b32..646ca25 100644 --- a/packages/builder-node/package.json +++ b/packages/builder-node/package.json @@ -2,6 +2,9 @@ "name": "@catalogjs/builder-node", "version": "0.0.1", "license": "MIT", + "scripts": { + "test": "qunit test/test-entrypoint.js" + }, "dependencies": { "@babel/core": "^7.9.0", "@babel/plugin-proposal-class-properties": "^7.10.4", @@ -18,6 +21,7 @@ "@catalogjs/loader": "0.0.1", "@catalogjs/polyfills": "0.0.1", "@catalogjs/recipes": "0.0.1", + "@embroider/core": "^0.36.0", "@types/fs-extra": "^8.1.0", "@types/node-fetch": "^2.5.7", "@types/resolve": "^1.17.1", diff --git a/packages/builder-node/src/build.ts b/packages/builder-node/src/build.ts index bcbdf39..e6a7532 100644 --- a/packages/builder-node/src/build.ts +++ b/packages/builder-node/src/build.ts @@ -139,6 +139,7 @@ async function build() { let builder = Builder.forProjects(fs, projectRoots, recipesURL, { bundle: { assigner }, + entrypoint: { suppressServiceWorkerLauncher: true }, }); await builder.build(); } diff --git a/packages/builder-node/src/install.ts b/packages/builder-node/src/install.ts index 07773cb..360c320 100644 --- a/packages/builder-node/src/install.ts +++ b/packages/builder-node/src/install.ts @@ -34,7 +34,7 @@ if (!pkgs || pkgs.filter(Boolean).length === 0) { ); process.exit(1); } -let projectDir = resolve(join(process.cwd(), project)); +let projectDir = resolve(process.cwd(), project); (async () => { // TODO this should probably come from the command line. the src cloning is a @@ -56,6 +56,7 @@ let projectDir = resolve(join(process.cwd(), project)); ); let recipesPath = join(resolveNodePkg("@catalogjs/recipes"), "recipes"); await fs.mount(recipesURL, new NodeFileSystemDriver(recipesPath)); + let builder = new Builder(fs, [builderRoot], recipesURL); await builder.build(); diff --git a/packages/builder-node/src/nodes/npm-import.ts b/packages/builder-node/src/nodes/npm-import.ts index 3664d38..fddb4d8 100644 --- a/packages/builder-node/src/nodes/npm-import.ts +++ b/packages/builder-node/src/nodes/npm-import.ts @@ -15,7 +15,7 @@ import { } from "./package"; import { PublishPackageNode } from "./publish"; import _glob from "glob"; -import { join } from "path"; +import { join, resolve } from "path"; import { resolveNodePkg } from "../pkg-resolve"; import { NodeFileSystemDriver } from "../node-filesystem-driver"; import { log } from "../../../builder-worker/src/logger"; @@ -24,6 +24,7 @@ import { Resolver } from "../../../builder-worker/src/resolver"; import { recipesURL } from "../../../builder-worker/src/recipes"; import { catalogjsHref } from "../../../builder-worker/src/resolver"; import { LockEntries } from "../../../builder-worker/src/nodes/lock-file"; +import { existsSync } from "fs"; export class NpmImportPackagesNode implements BuilderNode { // TODO the cache key for this should probably be some kind of lock file hash. @@ -71,7 +72,7 @@ export class NpmImportPackageNode implements BuilderNode { private pkgJSON: PackageJSON; constructor( name: string, - consumedFrom: string, + private consumedFrom: string, private workingDir: string, private resolver: Resolver ) { @@ -82,8 +83,27 @@ export class NpmImportPackageNode implements BuilderNode { ); } this.pkgPath = pkgPath; - this.pkgJSON = getPackageJSON(pkgPath); - this.cacheKey = `npm-import-pkg:${pkgPath}`; + let currentDir = pkgPath; + // our pkgPath may actually be an entrypoint that is deep in the pkg + // hierarchy, work our way up the pkgPath to find the package.json + while ( + (!existsSync(join(currentDir, "package.json")) || + !getPackageJSON(currentDir).name) && + currentDir.split("/").slice(-2)[0] !== "node_modules" + ) { + currentDir = resolve(currentDir, ".."); + } + if (!existsSync(join(currentDir, "package.json"))) { + throw new Error( + `Can't load package.json for ${name} at ${join( + pkgPath, + "package.json" + )}` + ); + } + this.pkgPath = currentDir; + this.pkgJSON = getPackageJSON(this.pkgPath); + this.cacheKey = `npm-import-pkg:${this.pkgPath}`; } async deps() { @@ -94,7 +114,8 @@ export class NpmImportPackageNode implements BuilderNode { workingPkgURL: new PreparePackageNode( this.pkgPath, this.pkgJSON, - this.workingDir + this.workingDir, + this.consumedFrom ), }; } diff --git a/packages/builder-node/src/nodes/package.ts b/packages/builder-node/src/nodes/package.ts index a5d117d..e6b8b0e 100644 --- a/packages/builder-node/src/nodes/package.ts +++ b/packages/builder-node/src/nodes/package.ts @@ -17,7 +17,7 @@ import childProcess from "child_process"; import { promisify } from "util"; import { ensureDirSync, existsSync, readJSONSync } from "fs-extra"; import fs from "fs"; -import { join } from "path"; +import { join, resolve } from "path"; import _glob from "glob"; import { WriteFileNode, @@ -26,6 +26,8 @@ import { import { SrcTransformNode } from "./src-transform"; import { Recipe } from "../../../builder-worker/src/recipes"; import { coerce } from "semver"; +import { transform, TransformOptions } from "@babel/core"; +import { applyVariantToTemplateCompiler, Variant } from "@embroider/core"; export const buildOutputDir = "__output/"; export const buildSrcDir = `__build_src/`; @@ -54,7 +56,8 @@ export class PreparePackageNode implements BuilderNode { constructor( private pkgPath: string, private pkgJSON: PackageJSON, - private workingDir: string + private workingDir: string, + private consumedFrom: string ) { this.cacheKey = `prepare-pkg:${pkgPath}`; } @@ -75,7 +78,8 @@ export class PreparePackageNode implements BuilderNode { this.pkgPath, pkgURL, this.pkgJSON, - this.workingDir + this.workingDir, + this.consumedFrom ), }; } @@ -133,21 +137,36 @@ class FinishPackagePreparationNode implements BuilderNode { private pkgPath: string, private pkgURL: URL, private pkgJSON: PackageJSON, - private workingDir: string + private workingDir: string, + private consumedFrom: string ) { this.cacheKey = `finish-pkg-preparation:${pkgPath}`; } - async deps() { + async deps(getRecipe: RecipeGetter) { let esCompliance = new MakePkgESCompliantNode( this.pkgURL, new PackageSrcNode( this.pkgJSON, this.pkgPath, this.pkgURL, - this.workingDir + this.workingDir, + this.consumedFrom ) ); + let { name, version } = this.pkgJSON; + let recipe = await getRecipe(name, version); + // all node builds have the runtime loader package available as a + // dependency, so that runtime loading situations (e.g. a dynamic + // require specifier) can be handled if they arise, as well as a + // polyfills package to polyfill node-isms + let deps: { [specifier: string]: string } = { + "@catalogjs/loader": "^0.0.1", + "@catalogjs/polyfills": "^0.0.1", + }; + if (recipe?.needsBabelRuntime) { + deps["@babel/runtime"] = "^7.13.0"; + } return { pkgPathFile: new WriteFileNode( new ConstantNode(this.pkgPath), @@ -157,14 +176,7 @@ class FinishPackagePreparationNode implements BuilderNode { this.pkgJSON, this.pkgURL, esCompliance, - // all node builds have the runtime loader package available as a - // dependency, so that runtime loading situations (e.g. a dynamic - // require specifier) can be handled if they arise, as well as a - // polyfills package to polyfill node-isms - { - "@catalogjs/loader": "^0.0.1", - "@catalogjs/polyfills": "^0.0.1", - } + deps ), esCompliance, }; @@ -181,7 +193,8 @@ export class PackageSrcNode implements BuilderNode { private pkgJSON: PackageJSON, private pkgPath: string, private pkgURL: URL, - private workingDir: string + private workingDir: string, + private consumedFrom: string ) { this.cacheKey = `pkg-source:${pkgPath}`; } @@ -192,7 +205,8 @@ export class PackageSrcNode implements BuilderNode { this.pkgJSON, this.pkgPath, this.pkgURL, - this.workingDir + this.workingDir, + this.consumedFrom ), }; } @@ -210,7 +224,8 @@ export class PackageSrcPrepareNode implements BuilderNode { private pkgJSON: PackageJSON, private pkgPath: string, private pkgURL: URL, - private workingDir: string + private workingDir: string, + private consumedFrom: string ) { this.cacheKey = `pkg-source-prepare:${pkgPath}`; } @@ -228,7 +243,7 @@ export class PackageSrcPrepareNode implements BuilderNode { srcPath = this.pkgPath; } let { srcIncludeGlob, srcIgnoreGlob } = recipe ?? {}; - srcIncludeGlob = srcIncludeGlob ?? "**/*.{ts,js,json}"; + srcIncludeGlob = srcIncludeGlob ?? "**/*.{ts,js,json,hbs}"; srcIgnoreGlob = srcIgnoreGlob ?? "{node_modules,test}/**"; let files = await glob(srcIncludeGlob, { @@ -236,12 +251,55 @@ export class PackageSrcPrepareNode implements BuilderNode { absolute: true, ignore: `${srcPath}/${srcIgnoreGlob}`, }); + + let babelConfig: TransformOptions | undefined; + if (recipe?.babelConfigPath) { + let babelConfigPath = resolve(this.consumedFrom, recipe.babelConfigPath); + log(`evaluating babel config ${babelConfigPath}`); + babelConfig = require(babelConfigPath); + } + let compile: ((filePath: string, template: string) => string) | undefined; + if (recipe?.templateCompilerPath) { + let variant: Variant = { + name: "default", + runtime: "all", + optimizeForProduction: false, + }; + let templateCompiler = require(join( + this.consumedFrom, + recipe.templateCompilerPath + )); + compile = applyVariantToTemplateCompiler( + variant, + templateCompiler.compile + ); + } + let contents = await Promise.all( files.map(async (file) => { let content = await readFile(file, "utf8"); for (let [macro, replacement] of Object.entries(macros ?? {})) { content = content.replace(new RegExp(macro, "g"), replacement); } + + if (babelConfig && file.endsWith(".js")) { + let output = transform(content, { ...babelConfig, filename: file }); + if (!output || output.code == null) { + throw new Error( + `Empty babel result after babel transform of ${file}` + ); + } + content = output.code; + } + if (file.endsWith(".hbs")) { + if (!compile) { + throw new Error( + `Encountered an .hbs file, ${file}, but there is no 'templateCompilerPath' specified in the recipe for ${name} ${version}` + ); + } + content = compile(file, content); + } + return content; }) ); @@ -255,6 +313,9 @@ export class PackageSrcPrepareNode implements BuilderNode { file.slice(srcPath.length), `${this.pkgURL}__stage1/` ); + if (url.href.endsWith(".hbs")) { + url = new URL(`${url.href}.js`); + } return new WriteFileNode(new ConstantNode(contents[index]), url); }) ), diff --git a/packages/builder-node/src/nodes/src-transform.ts b/packages/builder-node/src/nodes/src-transform.ts index 5ca36c1..4c85b34 100644 --- a/packages/builder-node/src/nodes/src-transform.ts +++ b/packages/builder-node/src/nodes/src-transform.ts @@ -38,8 +38,10 @@ export class SrcTransformNode implements BuilderNode { .filter( (entry) => entry.stat.type === "file" && - // TODO what about .ts? - (entry.url.href.endsWith(".js") || entry.url.href.endsWith(".json")) + !entry.url.href.endsWith(".d.ts") && + (entry.url.href.endsWith(".js") || + entry.url.href.endsWith(".ts") || + entry.url.href.endsWith(".json")) ) .map((entry) => entry.url); return { @@ -77,7 +79,7 @@ class BabelTransformNode implements BuilderNode { code = src; } else { let { name, version } = this.pkgJSON; - let { babelPlugins: plugins = [] } = + let { nodeBabelPlugins: plugins = [] } = (await getRecipe(name, version)) ?? {}; let output = transform(src, { plugins, comments: false }); if (!output || output.code == null) { @@ -92,7 +94,12 @@ class BabelTransformNode implements BuilderNode { `${this.pkgURL}__stage2/` ); return { - node: new WriteFileNode(new ConstantNode(code), url), + node: new WriteFileNode( + new ConstantNode(code), + url.href.endsWith(".ts") + ? new URL(url.href.replace(/\.ts$/, ".js")) + : url + ), }; } } diff --git a/packages/builder-node/src/resolver.ts b/packages/builder-node/src/resolver.ts index 365e691..40a6b40 100644 --- a/packages/builder-node/src/resolver.ts +++ b/packages/builder-node/src/resolver.ts @@ -18,6 +18,7 @@ import { FileSystem } from "../../builder-worker/src/filesystem"; import { FileDescriptor } from "../../builder-worker/src/filesystem-drivers/filesystem-driver"; import { FileNode } from "../../builder-worker/src/nodes/file"; import { EntrypointsJSON } from "../../builder-worker/src/nodes/entrypoint"; +import { maybeRelativeURL } from "../../builder-worker/src/path"; export class NodeResolver extends AbstractResolver { private pkgPathFileCache: Map = new Map(); @@ -26,12 +27,32 @@ export class NodeResolver extends AbstractResolver { } async resolveAsBuilderNode( specifier: string, - source: URL + source: URL, + projectInput: URL ): Promise> { if (specifier.startsWith(".") || specifier.startsWith("/")) { let resolution = await this.resolve(specifier, source); return new ConstantNode({ resolution, lockEntries: {} }); } + + // deal with a module that refers to a local import using it's own pkg name + let specifierPkgInfo = pkgInfoFromSpecifier(specifier); + let sourcePkgInfo = pkgInfoFromCatalogJsURL(source); + if ( + specifierPkgInfo?.pkgName && + specifierPkgInfo?.pkgName === sourcePkgInfo?.pkgName && + specifierPkgInfo.modulePath + ) { + let specifier = maybeRelativeURL( + new URL(`./${specifierPkgInfo.modulePath}`, projectInput), + source + ); + if (specifier.startsWith(".") || specifier.startsWith("/")) { + let resolution = await this.resolve(specifier, source); + return new ConstantNode({ resolution, lockEntries: {} }); + } + } + return new EnterDependencyNode( specifier, await this.getPkgPath(source), diff --git a/packages/builder-node/tsconfig.json b/packages/builder-node/tsconfig.json index 6a0a957..ccb9e91 100644 --- a/packages/builder-node/tsconfig.json +++ b/packages/builder-node/tsconfig.json @@ -18,6 +18,7 @@ "strictNullChecks": true, "strictPropertyInitialization": true, "noFallthroughCasesInSwitch": true, - "noImplicitReturns": true + "noImplicitReturns": true, + "skipLibCheck": true } } diff --git a/packages/builder-worker/entrypoints-schema.json b/packages/builder-worker/entrypoints-schema.json index 801c886..8727c89 100644 --- a/packages/builder-worker/entrypoints-schema.json +++ b/packages/builder-worker/entrypoints-schema.json @@ -57,8 +57,5 @@ "dependencies": { "$ref": "#/definitions/dependencies" } - }, - "required": [ - "name" - ] + } } \ No newline at end of file diff --git a/packages/builder-worker/package.json b/packages/builder-worker/package.json index 551aa56..62a60ab 100644 --- a/packages/builder-worker/package.json +++ b/packages/builder-worker/package.json @@ -40,6 +40,7 @@ "@types/babel__core": "^7.1.7", "@types/babel__traverse": "^7.0.10", "@types/diff": "^5.0.0", + "@types/glob-to-regexp": "^0.4.0", "@types/imurmurhash": "^0.1.1", "@types/json-stable-stringify": "^1.0.32", "@types/lodash": "^4.14.150", @@ -50,6 +51,7 @@ "diff": "^5.0.0", "dom-serializer": "^0.2.2", "fast-stable-stringify": "^1.0.0", + "glob-to-regexp": "^0.4.1", "globals": "^13.5.0", "htmlparser2": "^4.1.0", "idb-keyval": "^5.0.2", @@ -63,4 +65,4 @@ "volta": { "node": "14.5.0" } -} \ No newline at end of file +} diff --git a/packages/builder-worker/src/dependency-resolution.ts b/packages/builder-worker/src/dependency-resolution.ts index 9ad2d30..11669d9 100644 --- a/packages/builder-worker/src/dependency-resolution.ts +++ b/packages/builder-worker/src/dependency-resolution.ts @@ -799,11 +799,6 @@ function resolveDeclaration( pointer, depResolver )!; // this function will actually throw when a local desc that has an "original" is missing a resolution - if (resolution.importedSource) { - throw new Error( - `was expecting the resolution for '${declaration.declaredName}' in the module ${sourceModule.url.href} while building bundle ${bundle.href} to _not_ have a "importedSource" property but it did.` - ); - } let _module: Resolution; ({ consumedBy: _module, consumedByPointer: pointer } = resolution); sourceModule = makeNonCyclic(_module); diff --git a/packages/builder-worker/src/filesystem-drivers/http-driver.ts b/packages/builder-worker/src/filesystem-drivers/http-driver.ts index c55fef5..f1f3d3f 100644 --- a/packages/builder-worker/src/filesystem-drivers/http-driver.ts +++ b/packages/builder-worker/src/filesystem-drivers/http-driver.ts @@ -310,7 +310,7 @@ async function getIfNoneMatch( headers: { ...acceptHeader }, }); if (!response.ok) { - if (response.status === 404) { + if (response.status === 404 || response.status === 401) { throw new FileSystemError( "NOT_FOUND", `Cannot load underlying URL ${url}: ${ diff --git a/packages/builder-worker/src/nodes/append-module.ts b/packages/builder-worker/src/nodes/append-module.ts index 2926240..d5e93b7 100644 --- a/packages/builder-worker/src/nodes/append-module.ts +++ b/packages/builder-worker/src/nodes/append-module.ts @@ -764,6 +764,7 @@ function buildExports( exportRegions: Map; exportSpecifierRegions: Map; } { + let hasPreceedingNewline = code.length > 0; let { exports, reexports, exportAlls } = exportAssignments!; let reexportRegions: Map = new Map(); let exportRegions: Map = new Map(); @@ -795,6 +796,7 @@ function buildExports( regions, exportSpecifierRegions, bundleDeclarations, + hasPreceedingNewline, bundle ); } @@ -840,7 +842,7 @@ function buildExports( position: 0, firstChild: reexportDeclarationPointer + 1, nextSibling: undefined, - start: 1, // newline + start: hasPreceedingNewline ? 1 : 0, // newline end: 1, // ";" dependsOn: new Set([ ...[...[...mapping.keys()].entries()].map( @@ -923,7 +925,7 @@ function buildExports( position: 0, firstChild: regions.length + 1, nextSibling: undefined, - start: 1, // newline + start: hasPreceedingNewline ? 1 : 0, // newline end: 1, // ";" dependsOn: new Set([regions.length + 1]), preserveGaps: false, @@ -964,6 +966,7 @@ function buildExportNamedDeclaration( regions: CodeRegion[], specifierRegions: Map, bundleDeclarations: DeclarationRegionMap, + hasPreceedingNewline: boolean, bundle: URL ): RegionPointer | undefined { regions.push({ @@ -971,7 +974,7 @@ function buildExportNamedDeclaration( position: 0, firstChild: regions.length + 1, nextSibling: undefined, - start: 1, // newline + start: hasPreceedingNewline ? 1 : 0, // newline end: 3, // " };" dependsOn: new Set(), preserveGaps: false, diff --git a/packages/builder-worker/src/nodes/entrypoint.ts b/packages/builder-worker/src/nodes/entrypoint.ts index 6fdb978..2e00651 100644 --- a/packages/builder-worker/src/nodes/entrypoint.ts +++ b/packages/builder-worker/src/nodes/entrypoint.ts @@ -21,6 +21,10 @@ export interface Dependencies { [name: string]: Dependency; } +export interface EntrypointOptions { + suppressServiceWorkerLauncher: boolean; +} + export type Dependency = NpmDependency | CatalogJSDependency; export interface NpmDependency { @@ -56,7 +60,11 @@ export function depAsURL(dependency: Dependency): URL { export class EntrypointsJSONNode implements BuilderNode { cacheKey: string; - constructor(private input: URL, private output: URL) { + constructor( + private input: URL, + private output: URL, + private options?: EntrypointOptions + ) { this.cacheKey = `entrypoints-json:${this.input.href}:${this.output.href}`; } @@ -78,7 +86,8 @@ export class EntrypointsJSONNode implements BuilderNode { this.input, new URL(src, this.input), new URL(src, this.output), - dependencies + dependencies, + this.options ) ); } @@ -93,7 +102,8 @@ export class EntrypointNode implements BuilderNode { private projectRoot: URL, private src: URL, private dest: URL, - private dependencies: Dependencies + private dependencies: Dependencies, + private options?: EntrypointOptions ) { this.cacheKey = `entrypoint:${this.projectRoot.href}:${this.src.href}:${ this.dest.href @@ -131,7 +141,8 @@ export class EntrypointNode implements BuilderNode { this.src, this.dest, parsedHTML, - this.dependencies + this.dependencies, + this.options ), }; } else if (js) { @@ -173,7 +184,8 @@ export class HTMLEntrypoint { private src: URL, private dest: URL, private parsedHTML: dom.Node[], - private deps: Dependencies + private deps: Dependencies, + private options?: EntrypointOptions ) {} get destURL() { @@ -237,9 +249,11 @@ export class HTMLEntrypoint { // inject the service worker's js into the application entrypoint so that we // can communicate with the window (this allows us to trigger reloads after // rebuilds) - this.parsedHTML.push( - new dom.Element("script", { src: "https://service-worker/main.js" }) - ); + if (!this.options?.suppressServiceWorkerLauncher) { + this.parsedHTML.push( + new dom.Element("script", { src: "https://service-worker/main.js" }) + ); + } return render(this.parsedHTML); } } diff --git a/packages/builder-worker/src/nodes/project.ts b/packages/builder-worker/src/nodes/project.ts index df5937a..da22890 100644 --- a/packages/builder-worker/src/nodes/project.ts +++ b/packages/builder-worker/src/nodes/project.ts @@ -1,5 +1,10 @@ import { BuilderNode, NextNode, AllNode, ConstantNode, Value } from "./common"; -import { EntrypointsJSONNode, HTMLEntrypoint, Entrypoint } from "./entrypoint"; +import { + EntrypointsJSONNode, + HTMLEntrypoint, + Entrypoint, + EntrypointOptions, +} from "./entrypoint"; import { WriteFileNode } from "./file"; import uniqBy from "lodash/uniqBy"; import flatten from "lodash/flatten"; @@ -15,6 +20,7 @@ import { mapValues } from "lodash"; export interface Options { bundle?: BundleOptions; + entrypoint?: EntrypointOptions; seededResolutions?: { [specifier: string]: string }; } @@ -60,7 +66,8 @@ export class MakeProjectNode implements BuilderNode { ); let entrypoints = new EntrypointsJSONNode( this.inputRoot, - this.projectOutputRoot + this.projectOutputRoot, + this.options?.entrypoint ); return { entrypoints, diff --git a/packages/builder-worker/src/nodes/resolution.ts b/packages/builder-worker/src/nodes/resolution.ts index f4a43c6..4d2c7d9 100644 --- a/packages/builder-worker/src/nodes/resolution.ts +++ b/packages/builder-worker/src/nodes/resolution.ts @@ -19,6 +19,7 @@ import { File } from "@babel/types"; import { extractDescriptionFromSource } from "../description-encoder"; import { Resolver, pkgInfoFromCatalogJsURL } from "../resolver"; import { LockFile, GetLockFileNode, LockEntries } from "./lock-file"; +import globToRegExp from "glob-to-regexp"; export class ModuleResolutionsNode implements BuilderNode { cacheKey: string; @@ -65,6 +66,7 @@ export class ModuleResolutionsNode implements BuilderNode { new ModuleResolutionNode( new URL(jsEntrypoint), this.resolver, + this.projectInput, this.seededResolutions ) ); @@ -202,6 +204,7 @@ export class ModuleResolutionNode constructor( private url: URL, private resolver: Resolver, + private projectInput: URL, private lockEntries: LockEntries = {}, private stack: string[] = [] ) { @@ -221,14 +224,31 @@ export class ModuleResolutionNode }, getRecipe: RecipeGetter ): Promise> { + let recipeResolutions: { [specifier: string]: string } | undefined; + let { pkgName: sourcePkgName, version: sourcePkgVersion } = + pkgInfoFromCatalogJsURL(this.url) ?? {}; + if (sourcePkgName && sourcePkgVersion) { + recipeResolutions = (await getRecipe(sourcePkgName, sourcePkgVersion)) + ?.resolutions; + } + let urlNodes = await Promise.all( desc.imports.map(async (imp) => { - let { pkgName: sourcePkgName, version: sourcePkgVersion } = - pkgInfoFromCatalogJsURL(this.url) ?? {}; - if (sourcePkgName && sourcePkgVersion) { - let { resolutions } = - (await getRecipe(sourcePkgName, sourcePkgVersion)) ?? {}; - let href = resolutions?.[imp.specifier!]; + if (recipeResolutions) { + let specifierGlobs = Object.keys(recipeResolutions).filter((glob) => + globToRegExp(glob).test(imp.specifier!) + ); + if (specifierGlobs.length > 1) { + throw new Error( + `the specifier '${ + imp.specifier + }' matched multiple recipe resolutions: ${specifierGlobs.join( + ", " + )}. Please use specifier glob patterns that are more precise.` + ); + } + let [specifier] = specifierGlobs; + let href = specifier ? recipeResolutions[specifier] : undefined; if (href) { return new ConstantNode(new URL(href)); } @@ -245,6 +265,7 @@ export class ModuleResolutionNode desc, source, this.resolver, + this.projectInput, { ...this.lockEntries }, this.stack ), @@ -295,6 +316,7 @@ class FinishResolutionsFromLockNode implements BuilderNode { private consumerDesc: ModuleDescription, private consumerSource: string, private resolver: Resolver, + private projectInput: URL, private lockEntries: LockEntries, private stack: string[] ) { @@ -321,7 +343,8 @@ class FinishResolutionsFromLockNode implements BuilderNode { } return await this.resolver.resolveAsBuilderNode( imp.specifier!, - this.consumerURL + this.consumerURL, + this.projectInput ); }) ); @@ -332,6 +355,7 @@ class FinishResolutionsFromLockNode implements BuilderNode { this.consumerDesc, this.consumerSource, this.resolver, + this.projectInput, { ...this.lockEntries }, this.stack ), @@ -349,6 +373,7 @@ class FinishResolutionsFromResolverNode implements BuilderNode { private consumerDesc: ModuleDescription, private consumerSource: string, private resolver: Resolver, + private projectInput: URL, private lockEntries: LockEntries, private stack: string[] ) { @@ -378,10 +403,13 @@ class FinishResolutionsFromResolverNode implements BuilderNode { this.consumerURL.href, ]); } else { - return new ModuleResolutionNode(url, this.resolver, mergedLockEntries, [ - ...this.stack, - this.consumerURL.href, - ]); + return new ModuleResolutionNode( + url, + this.resolver, + this.projectInput, + mergedLockEntries, + [...this.stack, this.consumerURL.href] + ); } }); diff --git a/packages/builder-worker/src/recipes.ts b/packages/builder-worker/src/recipes.ts index 82df395..7d99f01 100644 --- a/packages/builder-worker/src/recipes.ts +++ b/packages/builder-worker/src/recipes.ts @@ -59,9 +59,19 @@ export interface Recipe { // By default the optional chaining and nullish coalescing operator plugins // will be used when performing the build, as well as we'll continue to add // other plugins for late stage TC39 proposals that are on the verge of - // becoming part of the spec. To utilize additional babel plugins, specify - // those in the "babelPlugins" property - babelPlugins?: (string | [string, { [optName: string]: any }])[]; + // becoming part of the spec. To utilize additional babel plugins as part of + // the node build, specify those in the "nodeBabelPlugins" property + nodeBabelPlugins?: (string | [string, { [optName: string]: any }])[]; + + // Optionally apply a custom babel configuration while importing an NPM + // package to catalogjs. The path provided is relative to the project specified + // when the node build was launched. + babelConfigPath?: string; + + // Optionally apply a Glimmer template compiler while importing an NPM + // package to catalogjs. The path provided is relative to the project specified + // when the node build was launched. + templateCompilerPath?: string; // If you wish to override the resolver and provide a specific resolution, // then use the "resolution" property. This is an object whose keys are @@ -99,6 +109,10 @@ export interface Recipe { macros?: { [macro: string]: string; }; + + // If this flag is set to true we will will include the @babel/runtime as a + // package dependency in the resulting entrypoints.json + needsBabelRuntime?: true; } let cache: Map = new Map(); diff --git a/packages/builder-worker/src/resolver.ts b/packages/builder-worker/src/resolver.ts index 3364b15..92cfe1c 100644 --- a/packages/builder-worker/src/resolver.ts +++ b/packages/builder-worker/src/resolver.ts @@ -10,7 +10,8 @@ export const workingHref = "https://working/"; export interface Resolver { resolveAsBuilderNode( specifier: string, - source: URL + source: URL, + projectInput: URL ): Promise>; resolve(specifier: string, source: URL): Promise; } @@ -50,7 +51,8 @@ export abstract class AbstractResolver implements Resolver { abstract resolveAsBuilderNode( specifier: string, - source: URL + source: URL, + projectInput: URL ): Promise>; async resolve(specifier: string, source: URL): Promise { @@ -97,6 +99,11 @@ export abstract class AbstractResolver implements Resolver { let candidateURL = new URL("index.js", makeURLEndInDir(url)); if (await this.fileExists(candidateURL)) { url = candidateURL; + } else if ( + (candidateURL = new URL(`${url.href}.hbs.js`)) && + (await this.fileExists(candidateURL)) + ) { + url = candidateURL; } } @@ -127,7 +134,11 @@ export class CoreResolver extends AbstractResolver { // URLs that match the project root input folder (currently in the Builder's // InternalFileNode) into here--this is a more natural place for // that. - async resolveAsBuilderNode(specifier: string, source: URL) { + async resolveAsBuilderNode( + specifier: string, + source: URL, + _projectInput: URL + ) { let resolution = await this.resolve(specifier, source); return new ConstantNode({ resolution, lockEntries: {} }); } diff --git a/packages/builder-worker/src/service-worker.ts b/packages/builder-worker/src/service-worker.ts index b25b2c8..dff8461 100644 --- a/packages/builder-worker/src/service-worker.ts +++ b/packages/builder-worker/src/service-worker.ts @@ -73,9 +73,9 @@ async function activate() { let uiDriver = new HttpFileSystemDriver(uiURL); let recipesDriver = new HttpFileSystemDriver(githubRecipesURL); let clientDriver = new FileDaemonClientDriver(originURL, websocketURL); - let [, clientVolume] = await Promise.all([ - fs.mount(new URL(`/catalogjs/ui/`, originURL), uiDriver), + let [clientVolume] = await Promise.all([ fs.mount(new URL("https://local-disk/"), clientDriver), + fs.mount(new URL(`/catalogjs/ui/`, originURL), uiDriver), fs.mount(recipesURL, recipesDriver), ]); diff --git a/packages/builder-worker/tsconfig.json b/packages/builder-worker/tsconfig.json index 527e74d..2e2fe86 100644 --- a/packages/builder-worker/tsconfig.json +++ b/packages/builder-worker/tsconfig.json @@ -19,6 +19,7 @@ "strictPropertyInitialization": true, "noFallthroughCasesInSwitch": true, "noImplicitReturns": true, - "lib": ["ES6", "ScriptHost", "WebWorker", "WebWorker.ImportScripts"] + "lib": ["ES6", "ScriptHost", "WebWorker", "WebWorker.ImportScripts"], + "skipLibCheck": true } } diff --git a/packages/builder-worker/webpack.config.js b/packages/builder-worker/webpack.config.js index a64354e..141074c 100644 --- a/packages/builder-worker/webpack.config.js +++ b/packages/builder-worker/webpack.config.js @@ -64,17 +64,6 @@ let dom = Object.assign({}, config, { }, }); -let node = Object.assign({}, config, { - target: "node", - entry: { - node: "../builder-node/src/index.ts", - "node-test": "../builder-node/test/index.ts", - }, - node: { - __dirname: false, - }, -}); - let worker = Object.assign({}, config, { target: "webworker", entry: { @@ -82,4 +71,4 @@ let worker = Object.assign({}, config, { }, }); -module.exports = [dom, worker, node]; +module.exports = [dom, worker]; diff --git a/packages/ember-app/.editorconfig b/packages/ember-app/.editorconfig new file mode 100644 index 0000000..c35a002 --- /dev/null +++ b/packages/ember-app/.editorconfig @@ -0,0 +1,19 @@ +# EditorConfig helps developers define and maintain consistent +# coding styles between different editors and IDEs +# editorconfig.org + +root = true + +[*] +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true +indent_style = space +indent_size = 2 + +[*.hbs] +insert_final_newline = false + +[*.{diff,md}] +trim_trailing_whitespace = false diff --git a/packages/ember-app/.ember-cli b/packages/ember-app/.ember-cli new file mode 100644 index 0000000..ee64cfe --- /dev/null +++ b/packages/ember-app/.ember-cli @@ -0,0 +1,9 @@ +{ + /** + Ember CLI sends analytics information by default. The data is completely + anonymous, but there are times when you might want to disable this behavior. + + Setting `disableAnalytics` to true will prevent any data from being sent. + */ + "disableAnalytics": false +} diff --git a/packages/ember-app/.eslintignore b/packages/ember-app/.eslintignore new file mode 100644 index 0000000..9221655 --- /dev/null +++ b/packages/ember-app/.eslintignore @@ -0,0 +1,21 @@ +# unconventional js +/blueprints/*/files/ +/vendor/ + +# compiled output +/dist/ +/tmp/ + +# dependencies +/bower_components/ +/node_modules/ + +# misc +/coverage/ +!.* +.eslintcache + +# ember-try +/.node_modules.ember-try/ +/bower.json.ember-try +/package.json.ember-try diff --git a/packages/ember-app/.eslintrc.js b/packages/ember-app/.eslintrc.js new file mode 100644 index 0000000..da079e3 --- /dev/null +++ b/packages/ember-app/.eslintrc.js @@ -0,0 +1,53 @@ +'use strict'; + +module.exports = { + root: true, + parser: 'babel-eslint', + parserOptions: { + ecmaVersion: 2018, + sourceType: 'module', + ecmaFeatures: { + legacyDecorators: true, + }, + }, + plugins: ['ember'], + extends: [ + 'eslint:recommended', + 'plugin:ember/recommended', + 'plugin:prettier/recommended', + ], + env: { + browser: true, + }, + rules: {}, + overrides: [ + // node files + { + files: [ + '.eslintrc.js', + '.prettierrc.js', + '.template-lintrc.js', + 'ember-cli-build.js', + 'testem.js', + 'blueprints/*/index.js', + 'config/**/*.js', + 'lib/*/index.js', + 'server/**/*.js', + ], + parserOptions: { + sourceType: 'script', + }, + env: { + browser: false, + node: true, + }, + plugins: ['node'], + extends: ['plugin:node/recommended'], + rules: { + // this can be removed once the following is fixed + // https://github.com/mysticatea/eslint-plugin-node/issues/77 + 'node/no-unpublished-require': 'off', + }, + }, + ], +}; diff --git a/packages/ember-app/.gitignore b/packages/ember-app/.gitignore new file mode 100644 index 0000000..7e0f7dd --- /dev/null +++ b/packages/ember-app/.gitignore @@ -0,0 +1,26 @@ +# See https://help.github.com/ignore-files/ for more about ignoring files. + +# compiled output +/dist/ +/tmp/ + +# dependencies +/bower_components/ +/node_modules/ + +# misc +/.env* +/.pnp* +/.sass-cache +/.eslintcache +/connect.lock +/coverage/ +/libpeerconnection.log +/npm-debug.log* +/testem.log +/yarn-error.log + +# ember-try +/.node_modules.ember-try/ +/bower.json.ember-try +/package.json.ember-try diff --git a/packages/ember-app/.prettierignore b/packages/ember-app/.prettierignore new file mode 100644 index 0000000..9221655 --- /dev/null +++ b/packages/ember-app/.prettierignore @@ -0,0 +1,21 @@ +# unconventional js +/blueprints/*/files/ +/vendor/ + +# compiled output +/dist/ +/tmp/ + +# dependencies +/bower_components/ +/node_modules/ + +# misc +/coverage/ +!.* +.eslintcache + +# ember-try +/.node_modules.ember-try/ +/bower.json.ember-try +/package.json.ember-try diff --git a/packages/ember-app/.prettierrc.js b/packages/ember-app/.prettierrc.js new file mode 100644 index 0000000..534e6d3 --- /dev/null +++ b/packages/ember-app/.prettierrc.js @@ -0,0 +1,5 @@ +'use strict'; + +module.exports = { + singleQuote: true, +}; diff --git a/packages/ember-app/.template-lintrc.js b/packages/ember-app/.template-lintrc.js new file mode 100644 index 0000000..3b0b9af --- /dev/null +++ b/packages/ember-app/.template-lintrc.js @@ -0,0 +1,5 @@ +'use strict'; + +module.exports = { + extends: 'octane', +}; diff --git a/packages/ember-app/.travis.yml b/packages/ember-app/.travis.yml new file mode 100644 index 0000000..5b5ce9d --- /dev/null +++ b/packages/ember-app/.travis.yml @@ -0,0 +1,25 @@ +--- +language: node_js +node_js: + - "10" + +dist: xenial + +addons: + chrome: stable + +cache: + directories: + - $HOME/.npm + +env: + global: + # See https://git.io/vdao3 for details. + - JOBS=1 + +branches: + only: + - master + +script: + - npm test diff --git a/packages/ember-app/.watchmanconfig b/packages/ember-app/.watchmanconfig new file mode 100644 index 0000000..e7834e3 --- /dev/null +++ b/packages/ember-app/.watchmanconfig @@ -0,0 +1,3 @@ +{ + "ignore_dirs": ["tmp", "dist"] +} diff --git a/packages/ember-app/README.md b/packages/ember-app/README.md new file mode 100644 index 0000000..dcbed1e --- /dev/null +++ b/packages/ember-app/README.md @@ -0,0 +1,57 @@ +# ember-app + +This README outlines the details of collaborating on this Ember application. +A short introduction of this app could easily go here. + +## Prerequisites + +You will need the following things properly installed on your computer. + +* [Git](https://git-scm.com/) +* [Node.js](https://nodejs.org/) (with npm) +* [Ember CLI](https://ember-cli.com/) +* [Google Chrome](https://google.com/chrome/) + +## Installation + +* `git clone ` this repository +* `cd ember-app` +* `npm install` + +## Running / Development + +* `ember serve` +* Visit your app at [http://localhost:4200](http://localhost:4200). +* Visit your tests at [http://localhost:4200/tests](http://localhost:4200/tests). + +### Code Generators + +Make use of the many generators for code, try `ember help generate` for more details + +### Running Tests + +* `ember test` +* `ember test --server` + +### Linting + +* `npm run lint:hbs` +* `npm run lint:js` +* `npm run lint:js -- --fix` + +### Building + +* `ember build` (development) +* `ember build --environment production` (production) + +### Deploying + +Specify what it takes to deploy your app. + +## Further Reading / Useful Links + +* [ember.js](https://emberjs.com/) +* [ember-cli](https://ember-cli.com/) +* Development Browser Extensions + * [ember inspector for chrome](https://chrome.google.com/webstore/detail/ember-inspector/bmdblncegkenkacieihfhpjfppoconhi) + * [ember inspector for firefox](https://addons.mozilla.org/en-US/firefox/addon/ember-inspector/) diff --git a/packages/ember-app/app/app.js b/packages/ember-app/app/app.js new file mode 100644 index 0000000..a9025d9 --- /dev/null +++ b/packages/ember-app/app/app.js @@ -0,0 +1,12 @@ +import Application from '@ember/application'; +import Resolver from 'ember-resolver'; +import loadInitializers from 'ember-load-initializers'; +import config from 'ember-app/config/environment'; + +export default class App extends Application { + modulePrefix = config.modulePrefix; + podModulePrefix = config.podModulePrefix; + Resolver = Resolver; +} + +loadInitializers(App, config.modulePrefix); diff --git a/packages/ember-app/app/components/.gitkeep b/packages/ember-app/app/components/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/packages/ember-app/app/controllers/.gitkeep b/packages/ember-app/app/controllers/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/packages/ember-app/app/helpers/.gitkeep b/packages/ember-app/app/helpers/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/packages/ember-app/app/index.html b/packages/ember-app/app/index.html new file mode 100644 index 0000000..df9dca7 --- /dev/null +++ b/packages/ember-app/app/index.html @@ -0,0 +1,30 @@ + + + + + + + EmberApp + + + + {{content-for "head"}} + + + + + {{content-for "head-footer"}} + + + {{content-for "body"}} + + + + + {{content-for "body-footer"}} + + diff --git a/packages/ember-app/app/models/.gitkeep b/packages/ember-app/app/models/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/packages/ember-app/app/router.js b/packages/ember-app/app/router.js new file mode 100644 index 0000000..99562de --- /dev/null +++ b/packages/ember-app/app/router.js @@ -0,0 +1,9 @@ +import EmberRouter from '@ember/routing/router'; +import config from 'ember-app/config/environment'; + +export default class Router extends EmberRouter { + location = config.locationType; + rootURL = config.rootURL; +} + +Router.map(function () {}); diff --git a/packages/ember-app/app/routes/.gitkeep b/packages/ember-app/app/routes/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/packages/ember-app/app/styles/app.css b/packages/ember-app/app/styles/app.css new file mode 100644 index 0000000..e69de29 diff --git a/packages/ember-app/app/templates/application.hbs b/packages/ember-app/app/templates/application.hbs new file mode 100644 index 0000000..9d268a6 --- /dev/null +++ b/packages/ember-app/app/templates/application.hbs @@ -0,0 +1,7 @@ +{{page-title "EmberApp"}} + +{{!-- The following component displays Ember's default welcome message. --}} + +{{!-- Feel free to remove this! --}} + +{{outlet}} \ No newline at end of file diff --git a/packages/ember-app/config/ember-cli-update.json b/packages/ember-app/config/ember-cli-update.json new file mode 100644 index 0000000..2a45f04 --- /dev/null +++ b/packages/ember-app/config/ember-cli-update.json @@ -0,0 +1,18 @@ +{ + "schemaVersion": "1.0.0", + "packages": [ + { + "name": "ember-cli", + "version": "3.25.0", + "blueprints": [ + { + "name": "app", + "outputRepo": "https://github.com/ember-cli/ember-new-output", + "codemodsSource": "ember-app-codemods-manifest@1", + "isBaseBlueprint": true, + "options": [] + } + ] + } + ] +} diff --git a/packages/ember-app/config/environment.js b/packages/ember-app/config/environment.js new file mode 100644 index 0000000..5a7adc9 --- /dev/null +++ b/packages/ember-app/config/environment.js @@ -0,0 +1,51 @@ +'use strict'; + +module.exports = function (environment) { + let ENV = { + modulePrefix: 'ember-app', + environment, + rootURL: '/', + locationType: 'auto', + EmberENV: { + FEATURES: { + // Here you can enable experimental features on an ember canary build + // e.g. EMBER_NATIVE_DECORATOR_SUPPORT: true + }, + EXTEND_PROTOTYPES: { + // Prevent Ember Data from overriding Date.parse. + Date: false, + }, + }, + + APP: { + // Here you can pass flags/options to your application instance + // when it is created + }, + }; + + if (environment === 'development') { + // ENV.APP.LOG_RESOLVER = true; + // ENV.APP.LOG_ACTIVE_GENERATION = true; + // ENV.APP.LOG_TRANSITIONS = true; + // ENV.APP.LOG_TRANSITIONS_INTERNAL = true; + // ENV.APP.LOG_VIEW_LOOKUPS = true; + } + + if (environment === 'test') { + // Testem prefers this... + ENV.locationType = 'none'; + + // keep test console output quieter + ENV.APP.LOG_ACTIVE_GENERATION = false; + ENV.APP.LOG_VIEW_LOOKUPS = false; + + ENV.APP.rootElement = '#ember-testing'; + ENV.APP.autoboot = false; + } + + if (environment === 'production') { + // here you can enable a production-specific feature + } + + return ENV; +}; diff --git a/packages/ember-app/config/optional-features.json b/packages/ember-app/config/optional-features.json new file mode 100644 index 0000000..b26286e --- /dev/null +++ b/packages/ember-app/config/optional-features.json @@ -0,0 +1,6 @@ +{ + "application-template-wrapper": false, + "default-async-observers": true, + "jquery-integration": false, + "template-only-glimmer-components": true +} diff --git a/packages/ember-app/config/targets.js b/packages/ember-app/config/targets.js new file mode 100644 index 0000000..4b33327 --- /dev/null +++ b/packages/ember-app/config/targets.js @@ -0,0 +1,18 @@ +'use strict'; + +const browsers = [ + 'last 1 Chrome versions', + 'last 1 Firefox versions', + 'last 1 Safari versions', +]; + +const isCI = Boolean(process.env.CI); +const isProduction = process.env.EMBER_ENV === 'production'; + +if (isCI || isProduction) { + browsers.push('ie 11'); +} + +module.exports = { + browsers, +}; diff --git a/packages/ember-app/ember-cli-build.js b/packages/ember-app/ember-cli-build.js new file mode 100644 index 0000000..814e6e6 --- /dev/null +++ b/packages/ember-app/ember-cli-build.js @@ -0,0 +1,18 @@ +'use strict'; + +const EmberApp = require('ember-cli/lib/broccoli/ember-app'); +const { compatBuild } = require('@embroider/compat'); + +module.exports = function (defaults) { + let app = new EmberApp(defaults, { + // Add options here + }); + + process.env.STAGE2_ONLY = true; + return compatBuild(app, null, { + staticComponents: true, + staticHelpers: true, + staticAddonTrees: true, + staticAddonTestSupportTrees: true, + }); +}; diff --git a/packages/ember-app/package.json b/packages/ember-app/package.json new file mode 100644 index 0000000..1164ee0 --- /dev/null +++ b/packages/ember-app/package.json @@ -0,0 +1,75 @@ +{ + "name": "ember-app", + "version": "0.0.0", + "private": true, + "description": "Small description for ember-app goes here", + "repository": "", + "license": "MIT", + "author": "", + "directories": { + "doc": "doc", + "test": "tests" + }, + "scripts": { + "build": "ember build", + "lint": "npm-run-all --aggregate-output --continue-on-error --parallel 'lint:!(fix)'", + "lint:fix": "npm-run-all --aggregate-output --continue-on-error --parallel lint:*:fix", + "lint:hbs": "ember-template-lint .", + "lint:hbs:fix": "ember-template-lint . --fix", + "lint:js": "eslint . --cache", + "lint:js:fix": "eslint . --fix", + "start": "ember serve --port=4250", + "start:catalogjs": "./start-catalogjs.sh", + "start:catalogjs-local-pkgs": "./start-catalogjs-local-pkgs.sh", + "test": "npm-run-all lint test:*", + "test:ember": "ember test" + }, + "devDependencies": { + "@ember/optional-features": "^2.0.0", + "@ember/test-helpers": "^2.2.0", + "@embroider/core": "^0.36.0", + "@embroider/compat": "^0.36.0", + "@glimmer/component": "^1.0.3", + "@glimmer/tracking": "^1.0.3", + "babel-eslint": "^10.1.0", + "broccoli-asset-rev": "^3.0.0", + "ember-auto-import": "^1.10.1", + "ember-cli": "~3.25.0", + "ember-cli-app-version": "^4.0.0", + "ember-cli-babel": "^7.23.1", + "ember-cli-dependency-checker": "^3.2.0", + "ember-cli-htmlbars": "^5.3.2", + "ember-cli-inject-live-reload": "^2.0.2", + "ember-cli-sri": "^2.1.1", + "ember-cli-terser": "^4.0.1", + "ember-export-application-global": "^2.0.1", + "ember-fetch": "^8.0.4", + "ember-load-initializers": "^2.1.2", + "ember-maybe-import-regenerator": "^0.1.6", + "ember-page-title": "^6.2.1", + "ember-qunit": "^5.1.2", + "ember-resolver": "^8.0.2", + "ember-source": "~3.25.1", + "ember-template-lint": "^2.18.1", + "ember-welcome-page": "^4.0.0", + "eslint": "^7.20.0", + "eslint-config-prettier": "^7.2.0", + "eslint-plugin-ember": "^10.2.0", + "eslint-plugin-node": "^11.1.0", + "eslint-plugin-prettier": "^3.3.1", + "loader.js": "^4.7.0", + "npm-run-all": "^4.1.5", + "prettier": "^2.2.1", + "qunit": "^2.14.0", + "qunit-dom": "^1.6.0" + }, + "engines": { + "node": "10.* || >= 12" + }, + "ember": { + "edition": "octane" + }, + "volta": { + "node": "14.16.0" + } +} \ No newline at end of file diff --git a/packages/ember-app/public/catalogjs.lock b/packages/ember-app/public/catalogjs.lock new file mode 100644 index 0000000..5909c35 --- /dev/null +++ b/packages/ember-app/public/catalogjs.lock @@ -0,0 +1,13 @@ +{ + "@babel/runtime/helpers/esm/defineProperty": "https://pkgs.catalogjs.com/npm/@babel/runtime/7.13.9/mBfk-1QW4gDBxqsJp2ixJH9zmJc=/helpers/esm/defineProperty.js", + "ember-resolver": "https://pkgs.catalogjs.com/npm/ember-resolver/8.0.2/GQqWCyuTYZpel1ax3h9ZXceGNfc=/index.js", + "ember-resolver/resolvers/classic/container-debug-adapter": "https://pkgs.catalogjs.com/npm/ember-resolver/8.0.2/GQqWCyuTYZpel1ax3h9ZXceGNfc=/resolvers/classic/container-debug-adapter.js", + "ember-load-initializers": "https://pkgs.catalogjs.com/npm/ember-load-initializers/2.1.2/wdp8Zi5xQIZWydN6Ucyr4h-SmXA=/index.js", + "@glimmer/component": "https://pkgs.catalogjs.com/npm/@glimmer/component/1.0.4/OXvFmuiY6mJ05Bu2AKFBAg-H7f0=/index.js", + "@glimmer/component/-private/ember-component-manager": "https://pkgs.catalogjs.com/npm/@glimmer/component/1.0.4/OXvFmuiY6mJ05Bu2AKFBAg-H7f0=/-private/ember-component-manager.js", + "ember-cli-app-version/initializer-factory": "https://pkgs.catalogjs.com/npm/ember-cli-app-version/4.0.0/rHZtfzZo6vl1+TSTDAUitzDe2N8=/initializer-factory.js", + "ember-page-title/helpers/page-title": "https://pkgs.catalogjs.com/npm/ember-page-title/6.2.1/Ockyp+mGheF75nmqUp04j2U46J0=/helpers/page-title.js", + "ember-page-title/services/page-title": "https://pkgs.catalogjs.com/npm/ember-page-title/6.2.1/Ockyp+mGheF75nmqUp04j2U46J0=/services/page-title.js", + "ember-page-title/services/page-title-list": "https://pkgs.catalogjs.com/npm/ember-page-title/6.2.1/Ockyp+mGheF75nmqUp04j2U46J0=/services/page-title-list.js", + "ember-welcome-page/components/welcome-page": "https://pkgs.catalogjs.com/npm/ember-welcome-page/4.0.0/sJN4XLBoKsdIpGhYSw6egTSPJco=/components/welcome-page.js" +} \ No newline at end of file diff --git a/packages/ember-app/public/entrypoints.json b/packages/ember-app/public/entrypoints.json new file mode 100644 index 0000000..f2bf722 --- /dev/null +++ b/packages/ember-app/public/entrypoints.json @@ -0,0 +1,43 @@ +{ + "$schema": "../../builder-worker/entrypoints-schema.json", + "html": [ + "index.html" + ], + "dependencies": { + "@babel/runtime": { + "type": "npm", + "pkgName": "@babel/runtime", + "range": "^7.13.0" + }, + "ember-resolver": { + "type": "npm", + "pkgName": "ember-resolver", + "range": "^8.0.2" + }, + "ember-load-initializers": { + "type": "npm", + "pkgName": "ember-load-initializers", + "range": "^2.1.2" + }, + "@glimmer/component": { + "type": "npm", + "pkgName": "@glimmer/component", + "range": "^1.0.3" + }, + "ember-cli-app-version": { + "type": "npm", + "pkgName": "ember-cli-app-version", + "range": "^4.0.0" + }, + "ember-page-title": { + "type": "npm", + "pkgName": "ember-page-title", + "range": "^6.2.1" + }, + "ember-welcome-page": { + "type": "npm", + "pkgName": "ember-welcome-page", + "range": "^4.0.0" + } + } +} \ No newline at end of file diff --git a/packages/ember-app/public/robots.txt b/packages/ember-app/public/robots.txt new file mode 100644 index 0000000..f591645 --- /dev/null +++ b/packages/ember-app/public/robots.txt @@ -0,0 +1,3 @@ +# http://www.robotstxt.org +User-agent: * +Disallow: diff --git a/packages/ember-app/start-catalogjs-local-pkgs.sh b/packages/ember-app/start-catalogjs-local-pkgs.sh new file mode 100755 index 0000000..7a1ce7d --- /dev/null +++ b/packages/ember-app/start-catalogjs-local-pkgs.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +node ../file-daemon/bin/file-daemon --builderServer=http://localhost:8080 --uiServer=http://localhost:4300/catalogjs/ui/ --pkgsPath=../../working/cdn/pkgs --ignore=node_modules $(cat dist/.stage2-output) diff --git a/packages/ember-app/start-catalogjs.sh b/packages/ember-app/start-catalogjs.sh new file mode 100755 index 0000000..ae51183 --- /dev/null +++ b/packages/ember-app/start-catalogjs.sh @@ -0,0 +1,4 @@ +#!/bin/sh + +## TODO use hosted recipes after this is merged to master +node ../file-daemon/bin/file-daemon --builderServer=http://localhost:8080 --uiServer=http://localhost:4300/catalogjs/ui/ --ignore=node_modules $(cat dist/.stage2-output) ../recipes diff --git a/packages/ember-app/testem.js b/packages/ember-app/testem.js new file mode 100644 index 0000000..ed2f371 --- /dev/null +++ b/packages/ember-app/testem.js @@ -0,0 +1,23 @@ +'use strict'; + +module.exports = { + test_page: 'tests/index.html?hidepassed', + disable_watching: true, + launch_in_ci: ['Chrome'], + launch_in_dev: ['Chrome'], + browser_start_timeout: 120, + browser_args: { + Chrome: { + ci: [ + // --no-sandbox is needed when running Chrome inside a container + process.env.CI ? '--no-sandbox' : null, + '--headless', + '--disable-dev-shm-usage', + '--disable-software-rasterizer', + '--mute-audio', + '--remote-debugging-port=0', + '--window-size=1440,900', + ].filter(Boolean), + }, + }, +}; diff --git a/packages/ember-app/tests/helpers/.gitkeep b/packages/ember-app/tests/helpers/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/packages/ember-app/tests/index.html b/packages/ember-app/tests/index.html new file mode 100644 index 0000000..83e5a84 --- /dev/null +++ b/packages/ember-app/tests/index.html @@ -0,0 +1,40 @@ + + + + + + EmberApp Tests + + + + {{content-for "head"}} + {{content-for "test-head"}} + + + + + + {{content-for "head-footer"}} + {{content-for "test-head-footer"}} + + + {{content-for "body"}} + {{content-for "test-body"}} + +
+
+
+
+
+
+ + + + + + + + {{content-for "body-footer"}} + {{content-for "test-body-footer"}} + + diff --git a/packages/ember-app/tests/integration/.gitkeep b/packages/ember-app/tests/integration/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/packages/ember-app/tests/test-helper.js b/packages/ember-app/tests/test-helper.js new file mode 100644 index 0000000..71ecf6a --- /dev/null +++ b/packages/ember-app/tests/test-helper.js @@ -0,0 +1,12 @@ +import Application from 'ember-app/app'; +import config from 'ember-app/config/environment'; +import * as QUnit from 'qunit'; +import { setApplication } from '@ember/test-helpers'; +import { setup } from 'qunit-dom'; +import { start } from 'ember-qunit'; + +setApplication(Application.create(config.APP)); + +setup(QUnit.assert); + +start(); diff --git a/packages/ember-app/tests/unit/.gitkeep b/packages/ember-app/tests/unit/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/packages/ember-app/vendor/.gitkeep b/packages/ember-app/vendor/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/packages/file-daemon-client/tsconfig.json b/packages/file-daemon-client/tsconfig.json index 0bba5a9..f2b8426 100644 --- a/packages/file-daemon-client/tsconfig.json +++ b/packages/file-daemon-client/tsconfig.json @@ -18,6 +18,7 @@ "strictPropertyInitialization": true, "noFallthroughCasesInSwitch": true, "noImplicitReturns": true, - "lib": ["ES6", "ScriptHost", "WebWorker", "WebWorker.ImportScripts"] + "lib": ["ES6", "ScriptHost", "WebWorker", "WebWorker.ImportScripts"], + "skipLibCheck": true } } diff --git a/packages/file-daemon/bin/cli.ts b/packages/file-daemon/bin/cli.ts index 4d4d28b..d4e3a3f 100644 --- a/packages/file-daemon/bin/cli.ts +++ b/packages/file-daemon/bin/cli.ts @@ -36,6 +36,11 @@ const argv = yargs.options({ description: "the path that contains packages to be served by a mock CDN which that would otherwise be found on the catalogjs CDN", }, + ignore: { + type: "string", + default: "", + description: "comma separated glob pattern of paths to ignore", + }, }).argv; if (argv._.length === 0) { diff --git a/packages/file-daemon/daemon.ts b/packages/file-daemon/daemon.ts index 2af826e..411aae6 100644 --- a/packages/file-daemon/daemon.ts +++ b/packages/file-daemon/daemon.ts @@ -4,8 +4,8 @@ import Koa from "koa"; import compose from "koa-compose"; import route, { KoaRoute } from "koa-better-route"; import { cors, serverLog, errorHandler } from "./koa-util"; -import { basename } from "path"; import send from "koa-send"; +import { Project } from "./project"; interface Options { port: number; @@ -15,30 +15,19 @@ interface Options { uiServer?: string; key?: string; pkgsPath?: string; -} - -export class ProjectMapping { - nameToPath: Map = new Map(); - pathToName: Map = new Map(); - constructor(directories: string[]) { - for (let dir of directories) { - let localName = basename(dir); - let counter = 0; - while (this.nameToPath.has(localName)) { - localName = `${localName}/${counter}`; - counter++; - } - this.nameToPath.set(localName, dir); - this.pathToName.set(dir, localName); - } - } + ignore: string; } export function start(opts: Options) { let { port, websocketPort, directories, pkgsPath } = opts; - let mapping = new ProjectMapping(directories); - new FileWatcherServer(websocketPort, mapping).start(); - let app = server({ mapping }, opts.builderServer, opts.uiServer); + let projects = Project.forDirs(directories); + new FileWatcherServer(websocketPort, projects).start(); + let app = server( + projects, + opts.ignore.split(","), + opts.builderServer, + opts.uiServer + ); app.listen(port); if (pkgsPath) { let pkgsPort = port + 1; @@ -50,7 +39,8 @@ export function start(opts: Options) { } export function server( - { mapping }: { mapping: ProjectMapping }, + projects: Project[], + ignore: string[] = [], builderServer?: string, uiServer?: string ) { @@ -63,7 +53,7 @@ export function server( route.get("/catalogjs/alive", (ctxt: KoaRoute.Context) => { ctxt.status = 200; }), - serveFiles(mapping, builderServer, uiServer), + serveFiles(projects, ignore, builderServer, uiServer), ]) ); return app; diff --git a/packages/file-daemon/file-hosting-server.ts b/packages/file-daemon/file-hosting-server.ts index 5e067d7..f88ebdc 100644 --- a/packages/file-daemon/file-hosting-server.ts +++ b/packages/file-daemon/file-hosting-server.ts @@ -11,14 +11,14 @@ import { DIRTYPE, REGTYPE } from "@catalogjs/tarstream/constants"; import { NodeReadableToDOM, DOMToNodeReadable } from "./stream-shims"; import { DirectoryEntry } from "@catalogjs/tarstream/types"; import { unixTime } from "./utils"; -import { join, resolve, dirname } from "path"; +import { join, resolve, dirname, extname } from "path"; import * as webStreams from "web-streams-polyfill/ponyfill/es2018"; import send from "koa-send"; import route, { KoaRoute } from "koa-better-route"; import compose from "koa-compose"; import proxy from "koa-proxies"; import flatMap from "lodash/flatMap"; -import { ProjectMapping } from "./daemon"; +import { Project } from "./project"; const catalogjsDist = resolve(join(__dirname, "..")); const builderDist = join(catalogjsDist, "builder"); @@ -28,17 +28,31 @@ const uiDist = join(catalogjsDist, "ui"); global = Object.assign(global, webStreams); export function serveFiles( - mapping: ProjectMapping, + projects: Project[], + ignore: string[] = [], builderServer?: string, uiServer?: string ) { return compose([ - ...flatMap([...mapping.nameToPath.entries()], ([localName, dir]) => { + ...flatMap(projects, (project) => { + let { localName, dir } = project; return [ route.get( `/catalogjs/files/${localName}/(.*)`, - (ctxt: KoaRoute.Context) => { - return send(ctxt, ctxt.routeParams[0], { root: dir }); + async (ctxt: KoaRoute.Context) => { + let source = project.loadFile(ctxt.routeParams[0]); + if (typeof source === "string") { + ctxt.set("Content-Length", String(source.length)); + ctxt.body = source; + ctxt.type = extname(ctxt.routeParams[0]); + } else if ("status" in source) { + if (source.message) { + ctxt.response.body = source.message; + } + ctxt.response.status = source.status; + } else { + await send(ctxt, source.streamFile, { root: dir }); + } } ), route.post(`/catalogjs/files/${localName}/(.*)`, updateFiles(dir)), @@ -47,7 +61,7 @@ export function serveFiles( }), route.get(`/catalogjs/files`, (ctxt: KoaRoute.Context) => { ctxt.res.setHeader("content-type", "application/x-tar"); - ctxt.body = streamFileSystem(mapping); + ctxt.body = streamFileSystem(projects, ignore); }), !builderServer @@ -107,27 +121,46 @@ export function serveFiles( ]); } -function streamFileSystem(mapping: ProjectMapping): Readable { +function streamFileSystem(projects: Project[], ignore: string[]): Readable { let tar = new Tar(); - for (let [localName, dir] of mapping.nameToPath) { - for (let entry of walkSync.entries(dir)) { - let { fullPath, size, mtime, mode, relativePath } = entry; + for (let project of projects) { + let { localName, dir } = project; + for (let entry of walkSync.entries(dir, { + ignore, + })) { + let { size, mtime, mode, relativePath } = entry; - relativePath = `${localName}/${relativePath}`; let file = { mode, size, modifyTime: unixTime(mtime), type: entry.isDirectory() ? DIRTYPE : REGTYPE, - name: entry.isDirectory() ? relativePath.slice(0, -1) : relativePath, }; if (entry.isDirectory()) { - tar.addFile(file as DirectoryEntry); + let name = `${localName}/${relativePath}`.slice(0, -1); + tar.addFile({ ...file, name } as DirectoryEntry); } else { - tar.addFile({ - ...file, - stream: () => new NodeReadableToDOM(createReadStream(fullPath)), - }); + for (let source of project.outputFiles(relativePath)) { + let loaded = source.load(); + if (typeof loaded === "string") { + tar.addFile({ + ...file, + name: `${localName}/${source.outputRelativePath}`, + data: Buffer.from(loaded, "utf8"), + }); + } else if ("status" in loaded) { + throw new Error( + `${localName} ${relativePath}->${source.outputRelativePath} generated error ${loaded.status} ${loaded.message}` + ); + } else { + let streamFile = resolve(project.dir, loaded.streamFile); + tar.addFile({ + ...file, + name: `${localName}/${source.outputRelativePath}`, + stream: () => new NodeReadableToDOM(createReadStream(streamFile)), + }); + } + } } } } diff --git a/packages/file-daemon/file-watcher-server.ts b/packages/file-daemon/file-watcher-server.ts index 29af867..af65e80 100644 --- a/packages/file-daemon/file-watcher-server.ts +++ b/packages/file-daemon/file-watcher-server.ts @@ -1,11 +1,9 @@ import WebSocket from "ws"; import sane, { Watcher } from "sane"; import { Stats } from "fs"; -import { ProjectMapping } from "./daemon"; +import { Project } from "./project"; import bind from "bind-decorator"; import { FileInfo } from "./interfaces"; -import { REGTYPE } from "@catalogjs/tarstream/constants"; -import { unixTime } from "./utils"; import flatMap from "lodash/flatMap"; export default class FileWatcherServer { @@ -17,12 +15,13 @@ export default class FileWatcherServer { // yet. private flushNotifications: Promise = Promise.resolve([]); - constructor(private port: number, mapping: ProjectMapping) { - for (let [name, path] of mapping.nameToPath) { - let watcher = sane(path); - watcher.on("add", this.notify("add", name)); - watcher.on("change", this.notify("change", name)); - watcher.on("delete", this.notify("delete", name)); + constructor(private port: number, projects: Project[]) { + for (let project of projects) { + let watcher = sane(project.dir); + let handler = this.notify(project); + watcher.on("add", handler); + watcher.on("change", handler); + watcher.on("delete", handler); this.watchers.push(watcher); } } @@ -51,28 +50,25 @@ export default class FileWatcherServer { } @bind - private notify(action: "add" | "change" | "delete", name: string) { + private notify(project: Project) { return (path: string, _root: string, stat?: Stats) => { if (this.watchers.length === 0 || (stat && stat.isDirectory())) { return; } - path = `${name}/${path}`; - let fileHash = stat ? `${stat.size}_${stat.mtime.valueOf()}` : null; - let info: FileInfo = { - name: path, - etag: fileHash, - }; - if (action !== "delete" && stat) { - info.header = { - name: path, - mode: stat.mode, - type: REGTYPE, - size: stat.size, - modifyTime: unixTime(stat.mtime.getTime()), + let paths = project + .outputFiles(path) + .map( + ({ outputRelativePath }) => + `${project.localName}/${outputRelativePath}` + ); + for (let outputPath of paths) { + let fileHash = stat ? `${stat.size}_${stat.mtime.valueOf()}` : null; + let info: FileInfo = { + name: outputPath, + etag: fileHash, }; + this.notificationQueue.push(info); } - - this.notificationQueue.push(info); (async () => await this.drainNotificationQueue())(); }; } diff --git a/packages/file-daemon/interfaces.ts b/packages/file-daemon/interfaces.ts index d635106..1235095 100644 --- a/packages/file-daemon/interfaces.ts +++ b/packages/file-daemon/interfaces.ts @@ -12,5 +12,4 @@ export interface FileHeader extends BaseFileEntry { export interface FileInfo { name: string; etag: string | null; // null means deleted - header?: FileHeader; // deleted files have no header } diff --git a/packages/file-daemon/package.json b/packages/file-daemon/package.json index e8a876b..a4d860c 100644 --- a/packages/file-daemon/package.json +++ b/packages/file-daemon/package.json @@ -6,10 +6,15 @@ "file-daemon": "./bin/file-daemon" }, "dependencies": { + "@babel/core": "^7.9.0", "@babel/plugin-proposal-decorators": "^7.8.3", "@catalogjs/tarstream": "0.0.1", + "@embroider/core": "^0.36.0", + "@types/babel__core": "^7.1.7", + "@types/debug": "^4.1.5", "@types/fs-extra": "^8.1.0", "@types/http-proxy": "^1.17.4", + "@types/jsdom": "^16.2.7", "@types/koa": "^2.11.3", "@types/koa-compose": "^3.2.5", "@types/koa-send": "^4.1.2", @@ -55,4 +60,4 @@ "volta": { "node": "14.5.0" } -} \ No newline at end of file +} diff --git a/packages/file-daemon/project.ts b/packages/file-daemon/project.ts new file mode 100644 index 0000000..f3e3871 --- /dev/null +++ b/packages/file-daemon/project.ts @@ -0,0 +1,131 @@ +import { basename, join } from "path"; +import { applyVariantToTemplateCompiler, Variant } from "@embroider/core"; +import { existsSync, readFileSync } from "fs-extra"; +import { transformSync } from "@babel/core"; + +export type FileSource = + | string + | { streamFile: string } + | { status: number; message?: string }; + +export class Project { + private compile: (path: string, template: string) => string; + constructor(public localName: string, public dir: string) { + let variant: Variant = { + name: "default", + runtime: "all", + optimizeForProduction: false, + }; + let templateCompilerFile = join(this.dir, "./_template_compiler_.js"); + if (existsSync(templateCompilerFile)) { + let templateCompiler = require(join( + this.dir, + "./_template_compiler_.js" + )); + this.compile = applyVariantToTemplateCompiler( + variant, + templateCompiler.compile + ); + } else { + this.compile = (template: string) => template; + } + } + + static forDirs(dirs: string[]): Project[] { + let usedNames = new Set(); + let projects: Project[] = []; + for (let dir of dirs) { + let localName = basename(dir); + let counter = 0; + while (usedNames.has(localName)) { + localName = `${localName}/${counter}`; + counter++; + } + projects.push(new Project(localName, dir)); + } + return projects; + } + + outputFiles( + inputRelativePath: string + ): { + outputRelativePath: string; + load: () => FileSource; + }[] { + if (inputRelativePath.endsWith(".js")) { + return [ + { + outputRelativePath: inputRelativePath, + load: applyBabel(this.dir, inputRelativePath), + }, + ]; + } + + if (inputRelativePath.endsWith(".hbs")) { + let filePath = join(this.dir, inputRelativePath); + let template = readFileSync(filePath, "utf8"); + return [ + { + outputRelativePath: `${inputRelativePath}.js`, + load: () => { + return this.compile(filePath, template); + }, + }, + ]; + } + + return [ + { + outputRelativePath: inputRelativePath, + load() { + return { streamFile: inputRelativePath }; + }, + }, + ]; + } + + loadFile(outputRelativePath: string): FileSource { + if (outputRelativePath.endsWith(".hbs")) { + return { status: 404 }; + } + + if (outputRelativePath.endsWith(".hbs.js")) { + let filePath = join(this.dir, outputRelativePath.slice(0, -3)); + let template = readFileSync(filePath, "utf8"); + return this.compile(filePath, template); + } + if (outputRelativePath.endsWith(".js")) { + return applyBabel(this.dir, outputRelativePath)(); + } + return { + streamFile: outputRelativePath, + }; + } +} + +function applyBabel(projectDir: string, relativePath: string): () => string { + return function () { + let filename = join(projectDir, relativePath); + // we're depending on embroider from branch catalogjs-experiment in the + // snowpack-experiment repo. However, the hack section in + // adjust-imports-plugin must be enabled when we're running babel here, but + // disabled when we're running it in the node build for importing npm + // dependencies. + let configFile = join(projectDir, "_babel_config_.js"); + if (existsSync(configFile)) { + console.log(`using babel config ${configFile}`); + let config = require(configFile); + // This is a hack that allows us to communicate to our embroider + // babel-config hack so that we don't have to hand edit it each time we add + // a new pkg dep, and rather just rely on the catalogjs.lock to understand + // our pkg deps + process.env.CATALOGJS_LOCK_FILE = join(projectDir, "catalogjs.lock"); + return transformSync(readFileSync(filename, "utf8"), { + ...config, + filename, + })!.code!; + } else { + return readFileSync(filename, "utf8"); + } + }; +} diff --git a/packages/file-daemon/test/file-hosting-test.ts b/packages/file-daemon/test/file-hosting-test.ts index 67b3ff7..fa8c5c0 100644 --- a/packages/file-daemon/test/file-hosting-test.ts +++ b/packages/file-daemon/test/file-hosting-test.ts @@ -1,16 +1,16 @@ import { serveFiles } from "../file-hosting-server"; import request from "supertest"; -import Project from "fixturify-project"; +import FixturifyProject from "fixturify-project"; import merge from "lodash/merge"; import Koa from "koa"; -import { ProjectMapping } from "../daemon"; +import { Project } from "../project"; const { test } = QUnit; QUnit.module("file-hosting", function (hooks) { - let project: Project; + let project: FixturifyProject; hooks.before(function () { - project = new Project("fixtures"); + project = new FixturifyProject("fixtures"); merge(project.files, { "test-app": { "index.html": "", @@ -35,7 +35,7 @@ QUnit.module("file-hosting", function (hooks) { let app = new Koa(); app.use( serveFiles( - new ProjectMapping([ + Project.forDirs([ `${project.root}/fixtures/test-app`, `${project.root}/fixtures/vendor/test-lib`, ]) diff --git a/packages/file-daemon/test/liveness-test.ts b/packages/file-daemon/test/liveness-test.ts index 62b3590..c222ca3 100644 --- a/packages/file-daemon/test/liveness-test.ts +++ b/packages/file-daemon/test/liveness-test.ts @@ -1,10 +1,11 @@ -import { server, ProjectMapping } from "../daemon"; +import { server } from "../daemon"; +import { Project } from "../project"; import request from "supertest"; const { test } = QUnit; QUnit.module("liveness endpoint", function () { test("responds", async function (assert) { - let app = server({ mapping: new ProjectMapping([]) }); + let app = server(Project.forDirs([])); let response = await request(app.callback()).get("/catalogjs/alive"); assert.equal(response.status, 200); assert.equal(response.get("access-control-allow-origin"), "*"); diff --git a/packages/file-daemon/tsconfig.json b/packages/file-daemon/tsconfig.json index 0b8375c..76e7759 100644 --- a/packages/file-daemon/tsconfig.json +++ b/packages/file-daemon/tsconfig.json @@ -17,7 +17,8 @@ "strictNullChecks": true, "strictPropertyInitialization": true, "noFallthroughCasesInSwitch": true, - "noImplicitReturns": true + "noImplicitReturns": true, + "skipLibCheck": true }, "include": ["./**/*.ts"] } diff --git a/packages/loader/entrypoints.json b/packages/loader/entrypoints.json index cd133b8..92fe6ee 100644 --- a/packages/loader/entrypoints.json +++ b/packages/loader/entrypoints.json @@ -1,6 +1,7 @@ { "$schema": "../builder-worker/entrypoints-schema.json", "js": [ - "./index.js" + "./index.js", + "require.js" ] } \ No newline at end of file diff --git a/packages/loader/index.ts b/packages/loader/index.ts index e9f232f..07c0f90 100644 --- a/packages/loader/index.ts +++ b/packages/loader/index.ts @@ -3,3 +3,4 @@ export { importHasNonStringLiteralSpecifier, requireHasNonStringLiteralSpecifier, } from "./non-string-literal-specifier"; +export { default as require } from "./require"; diff --git a/packages/loader/require.ts b/packages/loader/require.ts new file mode 100644 index 0000000..8ca66fe --- /dev/null +++ b/packages/loader/require.ts @@ -0,0 +1,3 @@ +export default function require(specifier: string) { + return window.require(specifier); +} diff --git a/packages/loader/tsconfig.json b/packages/loader/tsconfig.json index f323e69..be76982 100644 --- a/packages/loader/tsconfig.json +++ b/packages/loader/tsconfig.json @@ -12,6 +12,6 @@ "allowUnreachableCode": false, "noUnusedLocals": true, "noUnusedParameters": true, - "lib": ["ES2020", "ScriptHost"] + "lib": ["ES2020", "ScriptHost", "DOM"] } } diff --git a/packages/recipes/recipe-schema.json b/packages/recipes/recipe-schema.json index 8569115..7ddf743 100644 --- a/packages/recipes/recipe-schema.json +++ b/packages/recipes/recipe-schema.json @@ -42,10 +42,20 @@ "title": "Source Ignore Glob", "description": "This is a glob that describes all the files to ignore in the javascript build from the package. By default this is \"{node_modules,test}/**\", and can be overridden using the \"srcIgnoreGlob\" property." }, - "babelPlugins": { + "nodeBabelPlugins": { "$ref": "#/definitions/babelPlugins", - "title": "Babel Plugins", - "description": "By default the optional chaining and nullish coalescing operator plugins will be used when performing the build, as well as we'll continue to add other plugins for late stage TC39 proposals that are on the verge of becoming part of the spec. To utilize additional babel plugins, specify those in the \"babelPlugins\" property" + "title": "Node Babel Plugins", + "description": "By default the optional chaining and nullish coalescing operator plugins will be used when performing the build, as well as we'll continue to add other plugins for late stage TC39 proposals that are on the verge of becoming part of the spec. To utilize additional babel plugins as part of the node build, specify those in the \"nodeBabelPlugins\" property" + }, + "babelConfigPath": { + "type": "string", + "title": "Babel Config", + "description": "Optionally apply a custom babel configuration while importing an NPM package to catalogjs. The path provide is relative to the project specified when the node build was launched." + }, + "templateCompilerPath": { + "type": "string", + "title": "Template Compiler", + "description": "Optionally apply a Glimmer template compiler while importing an NPM package to catalogjs. The path provided is relative to the project specified when the node build was launched." }, "resolutions": { "$ref": "#/definitions/resolutions", @@ -61,6 +71,11 @@ "$ref": "#/definitions/macros", "title": "Macros", "description": "If you wish to replace macros with specific values at build time, then use the \"macros\" property. This is an object whose keys are the macros that you wish to replace, and whose corresponding values are the macro replacements. Note that macro is the body of a regex, so make sure to escape any regex chars. Also if you are replacing the macro with a string literal, make sure to include quotes in the macro value:\n \"MY_VERSION\": \"'5.6.4'\"" + }, + "needsBabelRuntime": { + "type": "boolean", + "title": "Babel Runtime", + "description": "If this flag is set to true we will will include the @babel/runtime as a package dependency in the resulting entrypoints.json." } }, "additionalProperties": false diff --git a/packages/recipes/recipes/@babel/code-frame.json b/packages/recipes/recipes/@babel/code-frame.json index 1d049cc..3b270e7 100644 --- a/packages/recipes/recipes/@babel/code-frame.json +++ b/packages/recipes/recipes/@babel/code-frame.json @@ -11,7 +11,7 @@ "entrypoints": [ "./src/index.js" ], - "babelPlugins": [ + "nodeBabelPlugins": [ "@babel/plugin-transform-flow-strip-types", [ "@babel/plugin-proposal-decorators", diff --git a/packages/recipes/recipes/@babel/core.json b/packages/recipes/recipes/@babel/core.json index 9179643..b341c58 100644 --- a/packages/recipes/recipes/@babel/core.json +++ b/packages/recipes/recipes/@babel/core.json @@ -25,7 +25,7 @@ "process\\.env\\.NODE_ENV": "'production'", "process\\.env\\.BABEL_ENV": "'production'" }, - "babelPlugins": [ + "nodeBabelPlugins": [ "@babel/plugin-transform-flow-strip-types", [ "@babel/plugin-proposal-decorators", diff --git a/packages/recipes/recipes/@babel/generator.json b/packages/recipes/recipes/@babel/generator.json index c5f13c7..cb94d0f 100644 --- a/packages/recipes/recipes/@babel/generator.json +++ b/packages/recipes/recipes/@babel/generator.json @@ -11,7 +11,7 @@ "entrypoints": [ "./src/index.js" ], - "babelPlugins": [ + "nodeBabelPlugins": [ "@babel/plugin-transform-flow-strip-types", [ "@babel/plugin-proposal-decorators", diff --git a/packages/recipes/recipes/@babel/helper-function-name.json b/packages/recipes/recipes/@babel/helper-function-name.json index 8419a0a..a534d4f 100644 --- a/packages/recipes/recipes/@babel/helper-function-name.json +++ b/packages/recipes/recipes/@babel/helper-function-name.json @@ -11,7 +11,7 @@ "entrypoints": [ "./src/index.js" ], - "babelPlugins": [ + "nodeBabelPlugins": [ "@babel/plugin-transform-flow-strip-types", [ "@babel/plugin-proposal-decorators", diff --git a/packages/recipes/recipes/@babel/helper-get-function-arity.json b/packages/recipes/recipes/@babel/helper-get-function-arity.json index 90db08f..708ee6f 100644 --- a/packages/recipes/recipes/@babel/helper-get-function-arity.json +++ b/packages/recipes/recipes/@babel/helper-get-function-arity.json @@ -11,7 +11,7 @@ "entrypoints": [ "./src/index.js" ], - "babelPlugins": [ + "nodeBabelPlugins": [ "@babel/plugin-transform-flow-strip-types", [ "@babel/plugin-proposal-decorators", diff --git a/packages/recipes/recipes/@babel/helper-member-expression-to-functions.json b/packages/recipes/recipes/@babel/helper-member-expression-to-functions.json index 713c802..1027ca1 100644 --- a/packages/recipes/recipes/@babel/helper-member-expression-to-functions.json +++ b/packages/recipes/recipes/@babel/helper-member-expression-to-functions.json @@ -11,7 +11,7 @@ "entrypoints": [ "./src/index.js" ], - "babelPlugins": [ + "nodeBabelPlugins": [ "@babel/plugin-transform-flow-strip-types", [ "@babel/plugin-proposal-decorators", diff --git a/packages/recipes/recipes/@babel/helper-module-imports.json b/packages/recipes/recipes/@babel/helper-module-imports.json index 8b9509d..ad2f42a 100644 --- a/packages/recipes/recipes/@babel/helper-module-imports.json +++ b/packages/recipes/recipes/@babel/helper-module-imports.json @@ -18,7 +18,7 @@ "util": "https://pkgs.catalogjs.com/@catalogjs/polyfills/0.0.1/util.js", "inherits": "https://pkgs.catalogjs.com/@catalogjs/polyfills/0.0.1/inherits.js" }, - "babelPlugins": [ + "nodeBabelPlugins": [ "@babel/plugin-transform-flow-strip-types", [ "@babel/plugin-proposal-decorators", diff --git a/packages/recipes/recipes/@babel/helper-module-transforms.json b/packages/recipes/recipes/@babel/helper-module-transforms.json index f965e88..f1c9e04 100644 --- a/packages/recipes/recipes/@babel/helper-module-transforms.json +++ b/packages/recipes/recipes/@babel/helper-module-transforms.json @@ -19,7 +19,7 @@ "inherits": "https://pkgs.catalogjs.com/@catalogjs/polyfills/0.0.1/inherits.js", "path": "https://pkgs.catalogjs.com/@catalogjs/polyfills/0.0.1/path.js" }, - "babelPlugins": [ + "nodeBabelPlugins": [ "@babel/plugin-transform-flow-strip-types", [ "@babel/plugin-proposal-decorators", diff --git a/packages/recipes/recipes/@babel/helper-optimise-call-expression.json b/packages/recipes/recipes/@babel/helper-optimise-call-expression.json index 447244f..398d72b 100644 --- a/packages/recipes/recipes/@babel/helper-optimise-call-expression.json +++ b/packages/recipes/recipes/@babel/helper-optimise-call-expression.json @@ -11,7 +11,7 @@ "entrypoints": [ "./src/index.js" ], - "babelPlugins": [ + "nodeBabelPlugins": [ "@babel/plugin-transform-flow-strip-types", [ "@babel/plugin-proposal-decorators", diff --git a/packages/recipes/recipes/@babel/helper-replace-supers.json b/packages/recipes/recipes/@babel/helper-replace-supers.json index 274ca31..6781e9a 100644 --- a/packages/recipes/recipes/@babel/helper-replace-supers.json +++ b/packages/recipes/recipes/@babel/helper-replace-supers.json @@ -11,7 +11,7 @@ "entrypoints": [ "./src/index.js" ], - "babelPlugins": [ + "nodeBabelPlugins": [ "@babel/plugin-transform-flow-strip-types", [ "@babel/plugin-proposal-decorators", diff --git a/packages/recipes/recipes/@babel/helper-simple-access.json b/packages/recipes/recipes/@babel/helper-simple-access.json index 2710250..13d638b 100644 --- a/packages/recipes/recipes/@babel/helper-simple-access.json +++ b/packages/recipes/recipes/@babel/helper-simple-access.json @@ -11,7 +11,7 @@ "entrypoints": [ "./src/index.js" ], - "babelPlugins": [ + "nodeBabelPlugins": [ "@babel/plugin-transform-flow-strip-types", [ "@babel/plugin-proposal-decorators", diff --git a/packages/recipes/recipes/@babel/helper-split-export-declaration.json b/packages/recipes/recipes/@babel/helper-split-export-declaration.json index 8f37135..f7120db 100644 --- a/packages/recipes/recipes/@babel/helper-split-export-declaration.json +++ b/packages/recipes/recipes/@babel/helper-split-export-declaration.json @@ -11,7 +11,7 @@ "entrypoints": [ "./src/index.js" ], - "babelPlugins": [ + "nodeBabelPlugins": [ "@babel/plugin-transform-flow-strip-types", [ "@babel/plugin-proposal-decorators", diff --git a/packages/recipes/recipes/@babel/helper-validator-identifier.json b/packages/recipes/recipes/@babel/helper-validator-identifier.json index 76459e1..a096b39 100644 --- a/packages/recipes/recipes/@babel/helper-validator-identifier.json +++ b/packages/recipes/recipes/@babel/helper-validator-identifier.json @@ -2,7 +2,7 @@ "$schema": "../../recipe-schema.json", "recipes": [ { - "semverRange": "*", + "semverRange": ">=7.12.11", "srcRepo": { "repoHref": "https://github.com/babel/babel.git", "version": "v$major$.$minor$.$patch$", @@ -18,7 +18,48 @@ "macros": { "new Set\\(": "new window.Set(" }, - "babelPlugins": [ + "nodeBabelPlugins": [ + "@babel/plugin-transform-typescript", + [ + "@babel/plugin-proposal-decorators", + { + "legacy": true + } + ], + [ + "@babel/plugin-proposal-class-properties", + { + "loose": true + } + ], + "@babel/plugin-proposal-numeric-separator", + [ + "@babel/plugin-proposal-pipeline-operator", + { + "proposal": "minimal" + } + ], + "@babel/plugin-proposal-logical-assignment-operators" + ] + }, + { + "semverRange": "<7.12.11", + "srcRepo": { + "repoHref": "https://github.com/babel/babel.git", + "version": "v$major$.$minor$.$patch$", + "subdir": "packages/babel-helper-validator-identifier/" + }, + "entrypoints": [ + "./src/index.js" + ], + "srcIgnoreGlob": "{node_modules,test,scripts}/**", + "dependencies": { + "charcodes": "^0.2.0" + }, + "macros": { + "new Set\\(": "new window.Set(" + }, + "nodeBabelPlugins": [ "@babel/plugin-transform-flow-strip-types", [ "@babel/plugin-proposal-decorators", diff --git a/packages/recipes/recipes/@babel/helpers.json b/packages/recipes/recipes/@babel/helpers.json index d8707f6..0e4388d 100644 --- a/packages/recipes/recipes/@babel/helpers.json +++ b/packages/recipes/recipes/@babel/helpers.json @@ -11,7 +11,7 @@ "entrypoints": [ "./src/index.js" ], - "babelPlugins": [ + "nodeBabelPlugins": [ "@babel/plugin-transform-flow-strip-types", [ "@babel/plugin-proposal-decorators", diff --git a/packages/recipes/recipes/@babel/highlight.json b/packages/recipes/recipes/@babel/highlight.json index dbed2d2..608ca22 100644 --- a/packages/recipes/recipes/@babel/highlight.json +++ b/packages/recipes/recipes/@babel/highlight.json @@ -11,7 +11,7 @@ "entrypoints": [ "./src/index.js" ], - "babelPlugins": [ + "nodeBabelPlugins": [ "@babel/plugin-transform-flow-strip-types", [ "@babel/plugin-proposal-decorators", diff --git a/packages/recipes/recipes/@babel/parser.json b/packages/recipes/recipes/@babel/parser.json index ebb0abe..4829c76 100644 --- a/packages/recipes/recipes/@babel/parser.json +++ b/packages/recipes/recipes/@babel/parser.json @@ -18,7 +18,7 @@ "@babel/helper-validator-identifier": "^7.9.0", "charcodes": "^0.2.0" }, - "babelPlugins": [ + "nodeBabelPlugins": [ "@babel/plugin-transform-flow-strip-types", [ "@babel/plugin-proposal-decorators", diff --git a/packages/recipes/recipes/@babel/runtime.json b/packages/recipes/recipes/@babel/runtime.json new file mode 100644 index 0000000..47ed533 --- /dev/null +++ b/packages/recipes/recipes/@babel/runtime.json @@ -0,0 +1,92 @@ +{ + "$schema": "../../recipe-schema.json", + "recipes": [ + { + "semverRange": "*", + "entrypoints": [ + "./helpers/esm/typeof.js", + "./helpers/esm/jsx.js", + "./helpers/esm/asyncIterator.js", + "./helpers/esm/AwaitValue.js", + "./helpers/esm/AsyncGenerator.js", + "./helpers/esm/wrapAsyncGenerator.js", + "./helpers/esm/awaitAsyncGenerator.js", + "./helpers/esm/asyncGeneratorDelegate.js", + "./helpers/esm/asyncToGenerator.js", + "./helpers/esm/classCallCheck.js", + "./helpers/esm/createClass.js", + "./helpers/esm/defineEnumerableProperties.js", + "./helpers/esm/defaults.js", + "./helpers/esm/defineProperty.js", + "./helpers/esm/extends.js", + "./helpers/esm/objectSpread.js", + "./helpers/esm/objectSpread2.js", + "./helpers/esm/inherits.js", + "./helpers/esm/inheritsLoose.js", + "./helpers/esm/getPrototypeOf.js", + "./helpers/esm/setPrototypeOf.js", + "./helpers/esm/isNativeReflectConstruct.js", + "./helpers/esm/construct.js", + "./helpers/esm/isNativeFunction.js", + "./helpers/esm/wrapNativeSuper.js", + "./helpers/esm/instanceof.js", + "./helpers/esm/interopRequireDefault.js", + "./helpers/esm/interopRequireWildcard.js", + "./helpers/esm/newArrowCheck.js", + "./helpers/esm/objectDestructuringEmpty.js", + "./helpers/esm/objectWithoutPropertiesLoose.js", + "./helpers/esm/objectWithoutProperties.js", + "./helpers/esm/assertThisInitialized.js", + "./helpers/esm/possibleConstructorReturn.js", + "./helpers/esm/createSuper.js", + "./helpers/esm/superPropBase.js", + "./helpers/esm/get.js", + "./helpers/esm/set.js", + "./helpers/esm/taggedTemplateLiteral.js", + "./helpers/esm/taggedTemplateLiteralLoose.js", + "./helpers/esm/readOnlyError.js", + "./helpers/esm/writeOnlyError.js", + "./helpers/esm/classNameTDZError.js", + "./helpers/esm/temporalUndefined.js", + "./helpers/esm/tdz.js", + "./helpers/esm/temporalRef.js", + "./helpers/esm/slicedToArray.js", + "./helpers/esm/slicedToArrayLoose.js", + "./helpers/esm/toArray.js", + "./helpers/esm/toConsumableArray.js", + "./helpers/esm/arrayWithoutHoles.js", + "./helpers/esm/arrayWithHoles.js", + "./helpers/esm/maybeArrayLike.js", + "./helpers/esm/iterableToArray.js", + "./helpers/esm/iterableToArrayLimit.js", + "./helpers/esm/iterableToArrayLimitLoose.js", + "./helpers/esm/unsupportedIterableToArray.js", + "./helpers/esm/arrayLikeToArray.js", + "./helpers/esm/nonIterableSpread.js", + "./helpers/esm/nonIterableRest.js", + "./helpers/esm/createForOfIteratorHelper.js", + "./helpers/esm/createForOfIteratorHelperLoose.js", + "./helpers/esm/skipFirstGeneratorNext.js", + "./helpers/esm/toPrimitive.js", + "./helpers/esm/toPropertyKey.js", + "./helpers/esm/initializerWarningHelper.js", + "./helpers/esm/initializerDefineProperty.js", + "./helpers/esm/applyDecoratedDescriptor.js", + "./helpers/esm/classPrivateFieldLooseKey.js", + "./helpers/esm/classPrivateFieldLooseBase.js", + "./helpers/esm/classPrivateFieldGet.js", + "./helpers/esm/classPrivateFieldSet.js", + "./helpers/esm/classPrivateFieldDestructureSet.js", + "./helpers/esm/classStaticPrivateFieldSpecGet.js", + "./helpers/esm/classStaticPrivateFieldSpecSet.js", + "./helpers/esm/classStaticPrivateMethodGet.js", + "./helpers/esm/classStaticPrivateMethodSet.js", + "./helpers/esm/decorate.js", + "./helpers/esm/classPrivateMethodGet.js", + "./helpers/esm/classPrivateMethodSet.js", + "./helpers/esm/wrapRegExp.js", + "./regenerator/index.js" + ] + } + ] +} \ No newline at end of file diff --git a/packages/recipes/recipes/@babel/template.json b/packages/recipes/recipes/@babel/template.json index 5078dd2..75c131a 100644 --- a/packages/recipes/recipes/@babel/template.json +++ b/packages/recipes/recipes/@babel/template.json @@ -11,7 +11,7 @@ "entrypoints": [ "./src/index.js" ], - "babelPlugins": [ + "nodeBabelPlugins": [ "@babel/plugin-transform-flow-strip-types", [ "@babel/plugin-proposal-decorators", diff --git a/packages/recipes/recipes/@babel/traverse.json b/packages/recipes/recipes/@babel/traverse.json index 6069653..2da195c 100644 --- a/packages/recipes/recipes/@babel/traverse.json +++ b/packages/recipes/recipes/@babel/traverse.json @@ -14,7 +14,7 @@ "macros": { "process\\.env\\.NODE_ENV": "'production'" }, - "babelPlugins": [ + "nodeBabelPlugins": [ "@babel/plugin-transform-flow-strip-types", [ "@babel/plugin-proposal-decorators", diff --git a/packages/recipes/recipes/@babel/types.json b/packages/recipes/recipes/@babel/types.json index 33a2408..56096bd 100644 --- a/packages/recipes/recipes/@babel/types.json +++ b/packages/recipes/recipes/@babel/types.json @@ -14,7 +14,7 @@ "macros": { "process\\.env\\.BABEL_TYPES_8_BREAKING": "false" }, - "babelPlugins": [ + "nodeBabelPlugins": [ "@babel/plugin-transform-flow-strip-types", [ "@babel/plugin-proposal-decorators", diff --git a/packages/recipes/recipes/@glimmer/component.json b/packages/recipes/recipes/@glimmer/component.json new file mode 100644 index 0000000..3d617dc --- /dev/null +++ b/packages/recipes/recipes/@glimmer/component.json @@ -0,0 +1,23 @@ +{ + "$schema": "../../recipe-schema.json", + "recipes": [ + { + "semverRange": "*", + "babelConfigPath": "_babel_config_.js", + "needsBabelRuntime": true, + "entrypoints": [ + "./index.js", + "./-private/ember-component-manager.js" + ], + "resolutions": { + "@babel/runtime/helpers/esm/classCallCheck": "https://pkgs.catalogjs.com/npm/@babel/runtime/7.13.9/mBfk-1QW4gDBxqsJp2ixJH9zmJc=/helpers/esm/classCallCheck.js", + "@babel/runtime/helpers/esm/createClass": "https://pkgs.catalogjs.com/npm/@babel/runtime/7.13.9/mBfk-1QW4gDBxqsJp2ixJH9zmJc=/helpers/esm/createClass.js", + "@babel/runtime/helpers/esm/get": "https://pkgs.catalogjs.com/npm/@babel/runtime/7.13.9/mBfk-1QW4gDBxqsJp2ixJH9zmJc=/helpers/esm/get.js", + "@babel/runtime/helpers/esm/inherits": "https://pkgs.catalogjs.com/npm/@babel/runtime/7.13.9/mBfk-1QW4gDBxqsJp2ixJH9zmJc=/helpers/esm/inherits.js", + "@babel/runtime/helpers/esm/possibleConstructorReturn": "https://pkgs.catalogjs.com/npm/@babel/runtime/7.13.9/mBfk-1QW4gDBxqsJp2ixJH9zmJc=/helpers/esm/possibleConstructorReturn.js", + "@babel/runtime/helpers/esm/getPrototypeOf": "https://pkgs.catalogjs.com/npm/@babel/runtime/7.13.9/mBfk-1QW4gDBxqsJp2ixJH9zmJc=/helpers/esm/getPrototypeOf.js", + "@babel/runtime/helpers/esm/typeof": "https://pkgs.catalogjs.com/npm/@babel/runtime/7.13.9/mBfk-1QW4gDBxqsJp2ixJH9zmJc=/helpers/esm/typeof.js" + } + } + ] +} \ No newline at end of file diff --git a/packages/recipes/recipes/charcodes.json b/packages/recipes/recipes/charcodes.json index 9c5a0bf..64672cb 100644 --- a/packages/recipes/recipes/charcodes.json +++ b/packages/recipes/recipes/charcodes.json @@ -7,7 +7,7 @@ "entrypoints": [ "./src/index.js" ], - "babelPlugins": [ + "nodeBabelPlugins": [ "@babel/plugin-transform-flow-strip-types", "@babel/plugin-transform-typescript", [ diff --git a/packages/recipes/recipes/ember-cli-app-version.json b/packages/recipes/recipes/ember-cli-app-version.json new file mode 100644 index 0000000..8bde5f5 --- /dev/null +++ b/packages/recipes/recipes/ember-cli-app-version.json @@ -0,0 +1,12 @@ +{ + "$schema": "../recipe-schema.json", + "recipes": [ + { + "semverRange": "*", + "babelConfigPath": "_babel_config_.js", + "entrypoints": [ + "./initializer-factory.js" + ] + } + ] +} \ No newline at end of file diff --git a/packages/recipes/recipes/ember-load-initializers.json b/packages/recipes/recipes/ember-load-initializers.json new file mode 100644 index 0000000..8056238 --- /dev/null +++ b/packages/recipes/recipes/ember-load-initializers.json @@ -0,0 +1,12 @@ +{ + "$schema": "../recipe-schema.json", + "recipes": [ + { + "semverRange": "*", + "babelConfigPath": "_babel_config_.js", + "resolutions": { + "*/require": "https://pkgs.catalogjs.com/@catalogjs/loader/0.0.1/require.js" + } + } + ] +} \ No newline at end of file diff --git a/packages/recipes/recipes/ember-page-title.json b/packages/recipes/recipes/ember-page-title.json new file mode 100644 index 0000000..9954e70 --- /dev/null +++ b/packages/recipes/recipes/ember-page-title.json @@ -0,0 +1,32 @@ +{ + "$schema": "../recipe-schema.json", + "recipes": [ + { + "semverRange": "*", + "babelConfigPath": "packages/ember-app/_babel_config_.js", + "templateCompilerPath": "packages/ember-app/_template_compiler_.js", + "needsBabelRuntime": true, + "entrypoints": [ + "./helpers/page-title.js", + "./services/page-title.js", + "./services/page-title-list.js", + "./test-support/get-page-title.js", + "./test-support/index.js" + ], + "resolutions": { + "@babel/runtime/helpers/esm/classCallCheck": "https://pkgs.catalogjs.com/npm/@babel/runtime/7.13.9/mBfk-1QW4gDBxqsJp2ixJH9zmJc=/helpers/esm/classCallCheck.js", + "@babel/runtime/helpers/esm/createClass": "https://pkgs.catalogjs.com/npm/@babel/runtime/7.13.9/mBfk-1QW4gDBxqsJp2ixJH9zmJc=/helpers/esm/createClass.js", + "@babel/runtime/helpers/esm/assertThisInitialized": "https://pkgs.catalogjs.com/npm/@babel/runtime/7.13.9/mBfk-1QW4gDBxqsJp2ixJH9zmJc=/helpers/esm/assertThisInitialized.js", + "@babel/runtime/helpers/esm/get": "https://pkgs.catalogjs.com/npm/@babel/runtime/7.13.9/mBfk-1QW4gDBxqsJp2ixJH9zmJc=/helpers/esm/get.js", + "@babel/runtime/helpers/esm/inherits": "https://pkgs.catalogjs.com/npm/@babel/runtime/7.13.9/mBfk-1QW4gDBxqsJp2ixJH9zmJc=/helpers/esm/inherits.js", + "@babel/runtime/helpers/esm/possibleConstructorReturn": "https://pkgs.catalogjs.com/npm/@babel/runtime/7.13.9/mBfk-1QW4gDBxqsJp2ixJH9zmJc=/helpers/esm/possibleConstructorReturn.js", + "@babel/runtime/helpers/esm/getPrototypeOf": "https://pkgs.catalogjs.com/npm/@babel/runtime/7.13.9/mBfk-1QW4gDBxqsJp2ixJH9zmJc=/helpers/esm/getPrototypeOf.js", + "@babel/runtime/helpers/esm/toConsumableArray": "https://pkgs.catalogjs.com/npm/@babel/runtime/7.13.9/mBfk-1QW4gDBxqsJp2ixJH9zmJc=/helpers/esm/toConsumableArray.js", + "@babel/runtime/helpers/esm/initializerDefineProperty": "https://pkgs.catalogjs.com/npm/@babel/runtime/7.13.9/mBfk-1QW4gDBxqsJp2ixJH9zmJc=/helpers/esm/initializerDefineProperty.js", + "@babel/runtime/helpers/esm/defineProperty": "https://pkgs.catalogjs.com/npm/@babel/runtime/7.13.9/mBfk-1QW4gDBxqsJp2ixJH9zmJc=/helpers/esm/defineProperty.js", + "@babel/runtime/helpers/esm/applyDecoratedDescriptor": "https://pkgs.catalogjs.com/npm/@babel/runtime/7.13.9/mBfk-1QW4gDBxqsJp2ixJH9zmJc=/helpers/esm/applyDecoratedDescriptor.js", + "@babel/runtime/helpers/esm/initializerWarningHelper": "https://pkgs.catalogjs.com/npm/@babel/runtime/7.13.9/mBfk-1QW4gDBxqsJp2ixJH9zmJc=/helpers/esm/initializerWarningHelper.js" + } + } + ] +} \ No newline at end of file diff --git a/packages/recipes/recipes/ember-resolver.json b/packages/recipes/recipes/ember-resolver.json new file mode 100644 index 0000000..f35851b --- /dev/null +++ b/packages/recipes/recipes/ember-resolver.json @@ -0,0 +1,18 @@ +{ + "$schema": "../recipe-schema.json", + "recipes": [ + { + "semverRange": "*", + "babelConfigPath": "_babel_config_.js", + "needsBabelRuntime": true, + "entrypoints": [ + "./index.js", + "./resolvers/classic/container-debug-adapter.js" + ], + "resolutions": { + "@babel/runtime/helpers/esm/classCallCheck": "https://pkgs.catalogjs.com/npm/@babel/runtime/7.13.9/mBfk-1QW4gDBxqsJp2ixJH9zmJc=/helpers/esm/classCallCheck.js", + "@babel/runtime/helpers/esm/createClass": "https://pkgs.catalogjs.com/npm/@babel/runtime/7.13.9/mBfk-1QW4gDBxqsJp2ixJH9zmJc=/helpers/esm/createClass.js" + } + } + ] +} \ No newline at end of file diff --git a/packages/recipes/recipes/ember-welcome-page.json b/packages/recipes/recipes/ember-welcome-page.json new file mode 100644 index 0000000..9cfeb12 --- /dev/null +++ b/packages/recipes/recipes/ember-welcome-page.json @@ -0,0 +1,14 @@ +{ + "$schema": "../recipe-schema.json", + "recipes": [ + { + "semverRange": "*", + "babelConfigPath": "packages/ember-app/_babel_config_.js", + "templateCompilerPath": "packages/ember-app/_template_compiler_.js", + "needsBabelRuntime": true, + "entrypoints": [ + "./components/welcome-page.js" + ] + } + ] +} \ No newline at end of file diff --git a/packages/shared/tsconfig.json b/packages/shared/tsconfig.json index f323e69..4d50157 100644 --- a/packages/shared/tsconfig.json +++ b/packages/shared/tsconfig.json @@ -12,6 +12,7 @@ "allowUnreachableCode": false, "noUnusedLocals": true, "noUnusedParameters": true, - "lib": ["ES2020", "ScriptHost"] + "lib": ["ES2020", "ScriptHost"], + "skipLibCheck": true } } diff --git a/packages/test-app/catalogjs.lock b/packages/test-app/catalogjs.lock index d4d8c2c..35b6d66 100644 --- a/packages/test-app/catalogjs.lock +++ b/packages/test-app/catalogjs.lock @@ -1,4 +1,4 @@ { - "@babel/core": "https://pkgs.catalogjs.com/npm/@babel/core/7.9.0/8dF0ufvmOS8l-8S703WOWw8XTbA=/src/index.js", + "@babel/core": "https://pkgs.catalogjs.com/npm/@babel/core/7.9.0/zMmVF21X2oGGmbWWu-Qub+aDQ3s=/src/index.js", "lodash/flatMap": "https://pkgs.catalogjs.com/npm/lodash/4.17.19/D+sAl39M6XPSe0sFT6ZU+RGGp3E=/flatMap.js" } \ No newline at end of file diff --git a/packages/test-app/package.json b/packages/test-app/package.json index 6de0f69..006a1d5 100644 --- a/packages/test-app/package.json +++ b/packages/test-app/package.json @@ -4,8 +4,8 @@ "version": "0.0.1", "private": true, "scripts": { - "start-pkg": "node ../file-daemon/dist/main.js . ../test-lib", - "start": "node ../file-daemon/bin/file-daemon --builderServer=http://localhost:8080 --uiServer=http://localhost:4300/catalogjs/ui/ . ../test-lib" + "start": "node ../file-daemon/bin/file-daemon --builderServer=http://localhost:8080 --uiServer=http://localhost:4300/catalogjs/ui/ . ../test-lib", + "start:local-pkgs": "node ../file-daemon/bin/file-daemon --builderServer=http://localhost:8080 --uiServer=http://localhost:4300/catalogjs/ui/ --pkgsPath=../../working/cdn/pkgs . ../test-lib" }, "volta": { "node": "14.5.0" diff --git a/packages/test-lib/package.json b/packages/test-lib/package.json index 920fbd4..75713d2 100644 --- a/packages/test-lib/package.json +++ b/packages/test-lib/package.json @@ -5,7 +5,8 @@ "private": true, "scripts": { "start-pkg": "node ../file-daemon/dist/main.js .", - "start": "node ../file-daemon/bin/file-daemon --builderServer http://localhost:8080 --uiServer http://localhost:4300/catalogjs/ui/ ." + "start": "node ../file-daemon/bin/file-daemon --builderServer=http://localhost:8080 --uiServer=http://localhost:4300/catalogjs/ui/ .", + "start:local-pkgs": "node ../file-daemon/bin/file-daemon --builderServer=http://localhost:8080 --uiServer=http://localhost:4300/catalogjs/ui/ --pkgsPath=../../working/cdn/pkgs ." }, "volta": { "node": "14.5.0" diff --git a/packages/ui/app/components/project-selector.hbs b/packages/ui/app/components/project-selector.hbs index 398f2a0..c5e822a 100644 --- a/packages/ui/app/components/project-selector.hbs +++ b/packages/ui/app/components/project-selector.hbs @@ -28,7 +28,7 @@