diff --git a/.eslintignore b/.eslintignore index b9ef98ff5..cf040d311 100644 --- a/.eslintignore +++ b/.eslintignore @@ -6,3 +6,4 @@ vendor/ public/ coverage/ *.ember-try +tests-self/ diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 23b6e4cdb..b05102401 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -84,7 +84,7 @@ jobs: tests: - name: "Ember Tests" + name: "Browser Tests" strategy: matrix: # os: [ubuntu-latest, macOS-latest, windows-latest] @@ -109,7 +109,7 @@ jobs: echo "NPM: $( npm --version )" echo "pnpm: $( pnpm --version )" - name: Test - run: pnpm turbo test:ember + run: pnpm turbo test:chrome test:firefox env: CI_BROWSER: ${{ matrix.ci_browser }} diff --git a/.gitignore b/.gitignore index 17b9a529d..4989d8ffb 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ # compiled output dist/ declarations/ +__screenshots__/ /tmp/ /out/ diff --git a/.node-version b/.node-version index 10fef252a..2bd5a0a98 100644 --- a/.node-version +++ b/.node-version @@ -1 +1 @@ -20.18 +22 diff --git a/apps/repl/package.json b/apps/repl/package.json index a5c97976e..2ce9a1a4b 100644 --- a/apps/repl/package.json +++ b/apps/repl/package.json @@ -24,7 +24,8 @@ "start": "ember serve -p 4201", "start:iframe": "pnpx http-server ./public -i -p 4204", "test:browserstack": "./scripts/browserstack.sh", - "test:ember": "ember test --test-port 0", + "test:chrome": "CI_BROWSER=Chrome ember test --test-port 0", + "test:firefox": "CI_BROWSER=Firefox ember test --test-port 0", "lint": "pnpm -w exec lint", "lint:js": "pnpm -w exec lint js", "lint:js:fix": "pnpm -w exec lint js:fix", diff --git a/apps/tutorial/package.json b/apps/tutorial/package.json index bc2fe4ff2..c0f0ed834 100644 --- a/apps/tutorial/package.json +++ b/apps/tutorial/package.json @@ -17,7 +17,8 @@ "lint:fix": "pnpm -w exec lint fix", "cf": "cd dist && npx wrangler pages dev --port 42000 ./", "start": "ember serve", - "test:ember": "ember test --test-port 0", + "test:chrome": "CI_BROWSER=Chrome ember test --test-port 0", + "test:firefox": "CI_BROWSER=Firefox ember test --test-port 0", "lint:js": "pnpm -w exec lint js", "lint:js:fix": "pnpm -w exec lint js:fix", "lint:hbs": "pnpm -w exec lint hbs", diff --git a/dev/package.json b/dev/package.json index d80c7b572..50b71cb22 100644 --- a/dev/package.json +++ b/dev/package.json @@ -46,5 +46,6 @@ "directory": "dev" }, "license": "MIT", - "author": "NullVoxPopuli" + "author": "NullVoxPopuli", + "version": "0.0.0" } diff --git a/package.json b/package.json index 360e39b8c..c00c4819f 100644 --- a/package.json +++ b/package.json @@ -42,8 +42,9 @@ "@nullvoxpopuli/eslint-configs": "^4.0.0", "@glimmer/component": "^2.0.0", "ember-element-helper": "^0.8.5", - "ember-auto-import": "^2.9.0", + "ember-auto-import": "^2.10.0", "ember-repl": "workspace:*", + "repl-sdk": "workspace:*", "ember-source": ">= 6.0.1", "webpack": ">= 5.92.0", "array-includes": "npm:@nolyfill/array-includes@^1", diff --git a/packages/app-support/limber-ui/test-app/package.json b/packages/app-support/limber-ui/test-app/package.json index e631c6695..d30a0b793 100644 --- a/packages/app-support/limber-ui/test-app/package.json +++ b/packages/app-support/limber-ui/test-app/package.json @@ -12,7 +12,8 @@ "scripts": { "lint:fix": "pnpm -w exec lint fix", "start": "ember serve", - "test:ember": "ember test --test-port 0", + "test:chrome": "CI_BROWSER=Chrome ember test --test-port 0", + "test:firefox": "CI_BROWSER=Firefox ember test --test-port 0", "lint": "pnpm -w exec lint", "lint:js": "pnpm -w exec lint js", "lint:js:fix": "pnpm -w exec lint js:fix", diff --git a/packages/ember-repl/addon/package.json b/packages/ember-repl/addon/package.json index 94d0c6a23..ab23d1ab7 100644 --- a/packages/ember-repl/addon/package.json +++ b/packages/ember-repl/addon/package.json @@ -76,6 +76,7 @@ "dependencies": { "@babel/helper-plugin-utils": "^7.25.7", "@babel/standalone": "^7.25.7", + "@ember/test-waiters": "^3.1.0", "@embroider/addon-shim": "1.8.9", "@embroider/macros": "1.16.9", "babel-import-util": "^3.0.0", diff --git a/packages/ember-repl/addon/src/compile/formats.ts b/packages/ember-repl/addon/src/compile/formats.ts index e148335a8..8e072753a 100644 --- a/packages/ember-repl/addon/src/compile/formats.ts +++ b/packages/ember-repl/addon/src/compile/formats.ts @@ -1,3 +1,4 @@ +// import { resolveImportMap } from './imports.ts'; import { invocationName } from './utils.ts'; import type { ExtractedCode } from './formats/markdown.ts'; @@ -21,6 +22,10 @@ export async function compileGJS( try { let { compileJS } = await import('./formats/gjs/index.ts'); + // let importMap2 = await resolveImportMap(gjsInput, importMap); + // + // console.log(importMap2); + return await compileJS(gjsInput, importMap); } catch (error) { return { error: error as Error, name: 'unknown' }; diff --git a/packages/ember-repl/addon/src/compile/imports.ts b/packages/ember-repl/addon/src/compile/imports.ts new file mode 100644 index 000000000..2b7196c18 --- /dev/null +++ b/packages/ember-repl/addon/src/compile/imports.ts @@ -0,0 +1,36 @@ +/** + * We can't use es-module-lexer because it wants syntatically valid + * JavaScript. + * + * import { parse } from 'es-module-lexer'; + * + * We may not have syntactically valid javascript, + * but it's still useful to pull the imported modules into the + * browser's module cache. + */ +// + +import type { EvalImportMap } from './types.ts'; + +export async function resolveImportMap(doc: string, importMap?: EvalImportMap) { + if (!importMap) { + return {}; + } + + return importMap; + + // const [imports] = await parse(doc); + // + // + // console.log(imports); + + // let result = {}; + + // for (let [key, maybeFn] of Object.entries(importMap)) { + // if (typeof maybeFn === 'function') { + // + // } + // } + + // return result; +} diff --git a/packages/ember-repl/addon/src/compile/index.ts b/packages/ember-repl/addon/src/compile/index.ts index 5f146fea5..3c8f8e9cb 100644 --- a/packages/ember-repl/addon/src/compile/index.ts +++ b/packages/ember-repl/addon/src/compile/index.ts @@ -1,3 +1,5 @@ +import { buildWaiter } from '@ember/test-waiters'; + import { cell, resource, resourceFactory } from 'ember-resources'; import { @@ -14,6 +16,8 @@ type Format = 'glimdown' | 'gjs' | 'hbs'; export const CACHE = new Map(); +const compileWaiter = buildWaiter('ember-repl::compile'); + interface Events { onSuccess: (component: ComponentLike) => Promise | unknown; onError: (error: string) => Promise | unknown; @@ -189,6 +193,8 @@ export function Compiled( let error = cell(); let result = cell(); + let token = compileWaiter.beginAsync(); + if (input) { compile(input, { // narrowing is hard here, but this is an implementation detail @@ -197,9 +203,11 @@ export function Compiled( result.current = component; ready.set(true); error.set(null); + compileWaiter.endAsync(token); }, onError: async (e) => { error.set(e); + compileWaiter.endAsync(token); }, onCompileStart: async () => { ready.set(false); diff --git a/packages/ember-repl/addon/src/compile/types.ts b/packages/ember-repl/addon/src/compile/types.ts index afbccc790..a1d5e5815 100644 --- a/packages/ember-repl/addon/src/compile/types.ts +++ b/packages/ember-repl/addon/src/compile/types.ts @@ -2,10 +2,29 @@ import type { ComponentLike } from '@glint/template'; import type { Pluggable } from 'unified'; export interface EvalImportMap { - [moduleName: string]: ScopeMap; + /** + * The name of the module to import and the value that will be imported. + * For example: + * ```js + * { + * 'my-library': () => import('my-library'), + * } + * ``` + * + * or, if you want to make a fake module, you may specify its exports + * ```js + * { + * 'my-library': { Foo, Bar } + * } + * ``` + */ + [moduleName: string]: ScopeMap | (() => Promise); } export interface ScopeMap { + /** + * Key-value pairs of values and their export names + */ [localName: string]: unknown; } diff --git a/packages/ember-repl/test-app/app/app.ts b/packages/ember-repl/test-app/app/app.ts index a353fd541..7b9b9c8ff 100644 --- a/packages/ember-repl/test-app/app/app.ts +++ b/packages/ember-repl/test-app/app/app.ts @@ -1,3 +1,5 @@ +import 'decorator-transforms/globals'; + import Application from '@ember/application'; import loadInitializers from 'ember-load-initializers'; diff --git a/packages/ember-repl/test-app/app/templates/application.gjs b/packages/ember-repl/test-app/app/templates/application.gjs new file mode 100644 index 000000000..e69de29bb diff --git a/packages/ember-repl/test-app/app/templates/application.hbs b/packages/ember-repl/test-app/app/templates/application.hbs deleted file mode 100644 index 5230580f8..000000000 --- a/packages/ember-repl/test-app/app/templates/application.hbs +++ /dev/null @@ -1,3 +0,0 @@ -

Welcome to Ember

- -{{outlet}} \ No newline at end of file diff --git a/packages/ember-repl/test-app/ember-cli-build.js b/packages/ember-repl/test-app/ember-cli-build.js index 9d13ec264..c7eb7fe78 100644 --- a/packages/ember-repl/test-app/ember-cli-build.js +++ b/packages/ember-repl/test-app/ember-cli-build.js @@ -9,10 +9,19 @@ module.exports = function (defaults) { const app = new EmberApp(defaults, { // Add options here trees: { - app: sideWatch('app', { watching: [path.join(__dirname, '../addon/dist')] }), + app: sideWatch('app', { + watching: [path.join(__dirname, '../addon/dist'), path.join(__dirname, '../../repl-sdk')], + }), }, 'ember-cli-babel': { enableTypeScriptTransform: true, + disableDecoratorTransforms: true, + }, + babel: { + plugins: [ + // add the new transform. + require.resolve('decorator-transforms'), + ], }, name: 'test-app', autoImport: { diff --git a/packages/ember-repl/test-app/package.json b/packages/ember-repl/test-app/package.json index 42896f9f2..5bf179638 100644 --- a/packages/ember-repl/test-app/package.json +++ b/packages/ember-repl/test-app/package.json @@ -24,12 +24,18 @@ "lint:prettier": "pnpm -w exec lint prettier" }, "dependencies": { + "@babel/standalone": "^7.26.2", "@shikijs/rehype": "^1.21.1", "@types/unist": "^3.0.2", "buffer": "^6.0.3", "common-tags": "^1.8.2", + "content-tag": "^3.0.0", + "decorator-transforms": "^2.3.0", + "ember-modifier": "^4.1.0", "ember-repl": "workspace:*", "ember-resources": "^7.0.1", + "ember-route-template": "^1.0.3", + "repl-sdk": "workspace:*", "unist-util-visit": "^5.0.0" }, "devDependencies": { @@ -58,6 +64,7 @@ "@types/rsvp": "^4.0.9", "@typescript-eslint/eslint-plugin": "^8.8.0", "@typescript-eslint/parser": "^8.8.0", + "babel-plugin-ember-template-compilation": "^2.3.0", "concurrently": "^9.0.1", "ember-auto-import": "^2.9.0", "ember-cli": "~5.12.0", diff --git a/packages/ember-repl/test-app/tests/rendering/compiled-test.gts b/packages/ember-repl/test-app/tests/rendering/compiled-test.gts index 33f452f5a..92ce0b288 100644 --- a/packages/ember-repl/test-app/tests/rendering/compiled-test.gts +++ b/packages/ember-repl/test-app/tests/rendering/compiled-test.gts @@ -1,25 +1,24 @@ import { renderSettled } from '@ember/renderer'; import { render, settled } from '@ember/test-helpers'; -import { module, test } from 'qunit'; +import QUnit, { module, test } from 'qunit'; import { setupRenderingTest } from 'ember-qunit'; import { stripIndent } from 'common-tags'; import { Compiled } from 'ember-repl'; +const { assert } = QUnit; +const { Boolean } = globalThis; + module('Rendering | Compiled()', function (hooks) { setupRenderingTest(hooks); - test('it works', async function (assert) { - let doc = stripIndent` - hello there - `; - + async function renderTest(doc, options = {}) { render(