diff --git a/.c8rc.json b/.c8rc.json
index 5d44abeee..3ecf54cb2 100644
--- a/.c8rc.json
+++ b/.c8rc.json
@@ -18,10 +18,10 @@
"checkCoverage": true,
- "statements": 80,
+ "statements": 75,
"branches": 85,
"functions": 85,
- "lines": 80,
+ "lines": 75,
"watermarks": {
"statements": [75, 85],
diff --git a/.eslintignore b/.eslintignore
index 8e786af06..216d536ef 100644
--- a/.eslintignore
+++ b/.eslintignore
@@ -3,5 +3,8 @@
**/node_modules/**
!.eslintrc.cjs
!.mocharc.js
+packages/init/src/template/**
+packages/init/test/cases/**/output/**
+packages/init/test/cases/**/my-app/**
packages/plugin-babel/test/cases/**/*main.js
TODO.md
\ No newline at end of file
diff --git a/.eslintrc.cjs b/.eslintrc.cjs
index 0c06c5089..dce41cebc 100644
--- a/.eslintrc.cjs
+++ b/.eslintrc.cjs
@@ -1,14 +1,27 @@
+// need this custom parser configuration until ESLint natively supports import attributes
+// https://github.com/eslint/eslint/discussions/15305#discussioncomment-2508948
module.exports = {
- parser: '@typescript-eslint/parser',
+ parser: '@babel/eslint-parser',
parserOptions: {
- ecmaVersion: 2018,
- sourceType: 'module'
+ ecmaVersion: 2022,
+ sourceType: 'module',
+ requireConfigFile: false,
+ ecmaFeatures: {
+ jsx: true
+ },
+ babelOptions: {
+ plugins: [
+ '@babel/plugin-syntax-import-assertions'
+ ],
+ presets: ['@babel/preset-react']
+ }
},
plugins: [
- '@typescript-eslint',
'no-only-tests'
],
- extends: 'plugin:markdown/recommended',
+ // plugin does not seem to work well with custom parsers?
+ // https://github.com/eslint/eslint-plugin-markdown/discussions/221
+ // extends: 'plugin:markdown/recommended-legacy',
env: {
browser: true,
node: false
diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md
index d3e92b598..90ed23df3 100644
--- a/.github/CONTRIBUTING.md
+++ b/.github/CONTRIBUTING.md
@@ -57,7 +57,7 @@ The [layout](https://github.com/ProjectEvergreen/greenwood/tree/master/packages/
- _lib/_ - Custom utility and client facing files
- _lifecycles/_ - Tasks that can be composed by commands to support the full needs of that command
- _plugins/_ - Custom default plugins maintained by the CLI project
-- _templates/_ - Default templates and / or pages provided by Greenwood.
+- _layouts/_ - Default layouts and / or pages provided by Greenwood.
#### Lifecycles
diff --git a/.github/workflows/ci-exp.yml b/.github/workflows/ci-loaders.yml
similarity index 89%
rename from .github/workflows/ci-exp.yml
rename to .github/workflows/ci-loaders.yml
index f4916828d..34c079cbd 100644
--- a/.github/workflows/ci-exp.yml
+++ b/.github/workflows/ci-loaders.yml
@@ -1,4 +1,4 @@
-name: Continuous Integration (Experimental)
+name: Continuous Integration (Loaders)
on: [pull_request]
@@ -25,4 +25,4 @@ jobs:
yarn install --frozen-lockfile && yarn lerna bootstrap
- name: Test
run: |
- yarn test:exp
\ No newline at end of file
+ yarn test:loaders
\ No newline at end of file
diff --git a/.github/workflows/ci-win-exp.yml b/.github/workflows/ci-win-loaders.yml
similarity index 85%
rename from .github/workflows/ci-win-exp.yml
rename to .github/workflows/ci-win-loaders.yml
index ffc7ef0fd..7e69f3873 100644
--- a/.github/workflows/ci-win-exp.yml
+++ b/.github/workflows/ci-win-loaders.yml
@@ -1,4 +1,4 @@
-name: Continuous Integration Windows (Experimental)
+name: Continuous Integration Windows (Loaders)
on: [pull_request]
@@ -22,4 +22,4 @@ jobs:
yarn install --frozen-lockfile --network-timeout 1000000 && yarn lerna bootstrap
- name: Test
run: |
- yarn test:exp:win
\ No newline at end of file
+ yarn test:loaders:win
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
index a56ab6ff6..5acf30c1f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -5,10 +5,12 @@
.vscode/
coverage/
node_modules/
+packages/init/test/**/my-app
+packages/init/test/**/output
packages/**/test/**/yarn.lock
packages/**/test/**/package-lock.json
packages/**/test/**/netlify
packages/**/test/**/.netlify
packages/**/test/**/.vercel
public/
-adapter-outlet/
\ No newline at end of file
+adapter-output/
\ No newline at end of file
diff --git a/.ls-lint.yml b/.ls-lint.yml
index 64ab822b4..ab2ad9cba 100644
--- a/.ls-lint.yml
+++ b/.ls-lint.yml
@@ -14,5 +14,6 @@ ls:
ignore:
- .git
- node_modules
+ - packages/plugin-babel/node_modules
- packages/init/node_modules
- packages/plugin-typescript/node_modules
\ No newline at end of file
diff --git a/.nvmrc b/.nvmrc
index 72c7744b3..23cc58a71 100644
--- a/.nvmrc
+++ b/.nvmrc
@@ -1 +1 @@
-18.12.1
\ No newline at end of file
+18.20.0
\ No newline at end of file
diff --git a/greenwood.config.js b/greenwood.config.js
index 73fc49abb..2ba3aca86 100644
--- a/greenwood.config.js
+++ b/greenwood.config.js
@@ -1,9 +1,8 @@
import { greenwoodPluginGraphQL } from '@greenwood/plugin-graphql';
import { greenwoodPluginIncludeHTML } from '@greenwood/plugin-include-html';
-import { greenwoodPluginImportCss } from '@greenwood/plugin-import-css';
-import { greenwoodPluginImportJson } from '@greenwood/plugin-import-json';
import { greenwoodPluginPolyfills } from '@greenwood/plugin-polyfills';
import { greenwoodPluginPostCss } from '@greenwood/plugin-postcss';
+import { greenwoodPluginImportRaw } from '@greenwood/plugin-import-raw';
import { greenwoodPluginRendererPuppeteer } from '@greenwood/plugin-renderer-puppeteer';
import rollupPluginAnalyzer from 'rollup-plugin-analyzer';
@@ -11,13 +10,19 @@ export default {
workspace: new URL('./www/', import.meta.url),
optimization: 'inline',
staticRouter: true,
- interpolateFrontmatter: true,
+ activeContent: true,
plugins: [
greenwoodPluginGraphQL(),
- greenwoodPluginPolyfills(),
+ greenwoodPluginPolyfills({
+ lit: true
+ }),
greenwoodPluginPostCss(),
- greenwoodPluginImportJson(),
- greenwoodPluginImportCss(),
+ greenwoodPluginImportRaw({
+ matches: [
+ 'eve-button.css',
+ 'eve-container.css'
+ ]
+ }),
greenwoodPluginIncludeHTML(),
greenwoodPluginRendererPuppeteer(),
{
diff --git a/lerna.json b/lerna.json
index b547049dd..3be4fc550 100644
--- a/lerna.json
+++ b/lerna.json
@@ -1,5 +1,5 @@
{
- "version": "0.29.4",
+ "version": "0.30.0-alpha.7",
"packages": [
"packages/*",
"www"
diff --git a/netlify.toml b/netlify.toml
index 3db7ff18d..5b5829366 100644
--- a/netlify.toml
+++ b/netlify.toml
@@ -10,4 +10,19 @@
[[redirects]]
from = "/docs/tech-stack/"
- to = "/about/tech-stack/"
\ No newline at end of file
+ to = "/about/tech-stack/"
+
+[[redirects]]
+ from = "/docs/menus/:splat"
+ to = "/docs/data/"
+ status = 200
+
+[[redirects]]
+ from = "/docs/data/#external-sources"
+ to = "/docs/data/#pages-data"
+ status = 200
+
+[[redirects]]
+ from = "/docs/data/#internal-sources"
+ to = "/docs/data/#pages-data"
+ status = 200
\ No newline at end of file
diff --git a/package.json b/package.json
index 71518641b..fe45e2b24 100644
--- a/package.json
+++ b/package.json
@@ -20,22 +20,24 @@
"build": "cross-env __GWD_ROLLUP_MODE__=strict node . build",
"serve": "node . serve",
"develop": "node . develop",
- "test": "cross-env BROWSERSLIST_IGNORE_OLD_DATA=true __GWD_ROLLUP_MODE__=strict NODE_NO_WARNINGS=1 c8 mocha --exclude \"./packages/**/test/cases/exp-*/**\" \"./packages/**/**/*.spec.js\"",
- "test:exp": "cross-env BROWSERSLIST_IGNORE_OLD_DATA=true __GWD_ROLLUP_MODE__=strict NODE_NO_WARNINGS=1 node --experimental-loader $(pwd)/test/test-loader.js ./node_modules/mocha/bin/mocha \"./packages/**/**/*.spec.js\"",
- "test:exp:win": "cross-env BROWSERSLIST_IGNORE_OLD_DATA=true __GWD_ROLLUP_MODE__=strict NODE_NO_WARNINGS=1 node --experimental-loader file:\\\\%cd%\\test\\test-loader.js ./node_modules/mocha/bin/mocha --exclude \"./packages/init/test/cases/**\" \"./packages/**/**/*.spec.js\"",
+ "test": "cross-env BROWSERSLIST_IGNORE_OLD_DATA=true __GWD_ROLLUP_MODE__=strict NODE_NO_WARNINGS=1 c8 mocha --exclude \"./packages/**/test/cases/loaders-*/**\" \"./packages/**/**/*.spec.js\"",
+ "test:loaders": "cross-env BROWSERSLIST_IGNORE_OLD_DATA=true __GWD_ROLLUP_MODE__=strict NODE_NO_WARNINGS=1 node --loader $(pwd)/test/test-loader.js ./node_modules/mocha/bin/mocha \"./packages/**/**/*.spec.js\"",
+ "test:loaders:win": "cross-env BROWSERSLIST_IGNORE_OLD_DATA=true __GWD_ROLLUP_MODE__=strict NODE_NO_WARNINGS=1 node --loader file:\\\\%cd%\\test\\test-loader.js ./node_modules/mocha/bin/mocha --exclude \"./packages/init/test/cases/**\" \"./packages/**/**/*.spec.js\"",
"test:tdd": "yarn test --watch",
- "lint:js": "eslint \"*.{js,md}\" \"./packages/**/**/*.{js,md}\" \"./test/*.js\" \"./www/**/**/*.{js,md}\"",
+ "lint:js": "eslint \"*.js\" \"./packages/**/**/*.js\" \"./test/*.js\" \"./www/**/**/*.js\"",
"lint:ts": "eslint \"./packages/**/**/*.ts\"",
"lint:css": "stylelint \"./www/**/*.js\", \"./www/**/*.css\"",
- "lint": "ls-lint && yarn lint:js && yarn lint:ts && yarn lint:css"
+ "lint": "ls-lint && yarn lint:js && yarn lint:css"
},
"resolutions": {
- "lit": "^2.1.1"
+ "lit": "^3.1.0"
},
"devDependencies": {
+ "@babel/core": "^7.24.4",
+ "@babel/eslint-parser": "^7.24.1",
+ "@babel/plugin-syntax-import-assertions": "^7.24.1",
+ "@babel/preset-react": "^7.24.1",
"@ls-lint/ls-lint": "^1.10.0",
- "@typescript-eslint/eslint-plugin": "^6.7.5",
- "@typescript-eslint/parser": "^6.7.5",
"babel-eslint": "^10.1.0",
"c8": "^7.10.0",
"chai": "^4.2.0",
diff --git a/packages/cli/package.json b/packages/cli/package.json
index 605c481e3..7e99dc26f 100644
--- a/packages/cli/package.json
+++ b/packages/cli/package.json
@@ -1,6 +1,6 @@
{
"name": "@greenwood/cli",
- "version": "0.29.4",
+ "version": "0.30.0-alpha.7",
"description": "Greenwood CLI.",
"type": "module",
"repository": "https://github.com/ProjectEvergreen/greenwood",
@@ -18,7 +18,7 @@
"NodeJS"
],
"engines": {
- "node": ">=18.12.1"
+ "node": ">=18.20.0"
},
"bin": {
"greenwood": "./src/index.js"
@@ -30,15 +30,16 @@
"access": "public"
},
"dependencies": {
- "@rollup/plugin-commonjs": "^21.0.0",
- "@rollup/plugin-node-resolve": "^13.0.0",
- "@rollup/plugin-replace": "^2.3.4",
- "@rollup/plugin-terser": "^0.1.0",
+ "@rollup/plugin-commonjs": "^25.0.0",
+ "@rollup/plugin-node-resolve": "^15.0.0",
+ "@rollup/plugin-replace": "^5.0.5",
+ "@rollup/plugin-terser": "^0.4.4",
"acorn": "^8.0.1",
+ "acorn-import-attributes": "^1.9.5",
"acorn-walk": "^8.0.0",
"commander": "^2.20.0",
- "css-tree": "^2.2.1",
- "es-module-shims": "^1.2.0",
+ "css-tree": "^3.0.0",
+ "es-module-shims": "^1.8.3",
"front-matter": "^4.0.2",
"koa": "^2.13.0",
"koa-body": "^6.0.1",
@@ -50,9 +51,9 @@
"remark-frontmatter": "^2.0.0",
"remark-parse": "^8.0.3",
"remark-rehype": "^7.0.0",
- "rollup": "^2.58.0",
+ "rollup": "^3.29.4",
"unified": "^9.2.0",
- "wc-compiler": "~0.10.0"
+ "wc-compiler": "~0.15.0"
},
"devDependencies": {
"@babel/runtime": "^7.10.4",
@@ -62,7 +63,8 @@
"@material/mwc-button": "^0.25.2",
"@stencil/core": "^2.12.0",
"@types/trusted-types": "^2.0.2",
- "lit": "^2.0.0",
+ "geist": "^1.2.0",
+ "lit": "^3.1.0",
"lit-redux-router": "~0.20.0",
"lodash-es": "^4.17.20",
"postcss-nested": "^4.1.2",
diff --git a/packages/cli/src/commands/build.js b/packages/cli/src/commands/build.js
index fba4ef73f..87f13e2d1 100644
--- a/packages/cli/src/commands/build.js
+++ b/packages/cli/src/commands/build.js
@@ -1,72 +1,17 @@
import { bundleCompilation } from '../lifecycles/bundle.js';
-import { checkResourceExists, trackResourcesForRoute } from '../lib/resource-utils.js';
+import { checkResourceExists } from '../lib/resource-utils.js';
import { copyAssets } from '../lifecycles/copy.js';
+import { getDevServer } from '../lifecycles/serve.js';
import fs from 'fs/promises';
import { preRenderCompilationWorker, preRenderCompilationCustom, staticRenderCompilation } from '../lifecycles/prerender.js';
import { ServerInterface } from '../lib/server-interface.js';
-// TODO a lot of these are duplicated in the prerender lifecycle too
-// would be good to refactor
-async function servePage(url, request, plugins) {
- let response = new Response('');
-
- for (const plugin of plugins) {
- if (plugin.shouldServe && await plugin.shouldServe(url, request)) {
- response = await plugin.serve(url, request);
- break;
- }
- }
-
- return response;
-}
-
-async function interceptPage(url, request, plugins, body) {
- let response = new Response(body, {
- headers: new Headers({ 'Content-Type': 'text/html' })
- });
-
- for (const plugin of plugins) {
- if (plugin.shouldIntercept && await plugin.shouldIntercept(url, request, response)) {
- response = await plugin.intercept(url, request, response);
- }
- }
-
- return response;
-}
-
-function getPluginInstances (compilation) {
- return [...compilation.config.plugins]
- .filter(plugin => plugin.type === 'resource' && plugin.name !== 'plugin-node-modules:resource')
- .map((plugin) => {
- return plugin.provider(compilation);
- });
-}
-
-// TODO does this make more sense in bundle lifecycle?
-// https://github.com/ProjectEvergreen/greenwood/issues/970
-// or could this be done sooner (like in appTemplate building in html resource plugin)?
-// Or do we need to ensure userland code / plugins have gone first
-async function trackResourcesForRoutes(compilation) {
- const plugins = getPluginInstances(compilation);
-
- for (const page of compilation.graph) {
- const { route } = page;
- const url = new URL(`http://localhost:${compilation.config.port}${route}`);
- const request = new Request(url);
-
- let body = await (await servePage(url, request, plugins)).text();
- body = await (await interceptPage(url, request, plugins, body)).text();
-
- await trackResourcesForRoute(body, compilation, route);
- }
-}
-
const runProductionBuild = async (compilation) => {
return new Promise(async (resolve, reject) => {
try {
- const { prerender } = compilation.config;
+ const { prerender, activeContent, plugins } = compilation.config;
const outputDir = compilation.context.outputDir;
const prerenderPlugin = compilation.config.plugins.find(plugin => plugin.type === 'renderer')
? compilation.config.plugins.find(plugin => plugin.type === 'renderer').provider(compilation)
@@ -74,6 +19,7 @@ const runProductionBuild = async (compilation) => {
const adapterPlugin = compilation.config.plugins.find(plugin => plugin.type === 'adapter')
? compilation.config.plugins.find(plugin => plugin.type === 'adapter').provider(compilation)
: null;
+ const shouldPrerender = prerender || prerenderPlugin.prerender;
if (!await checkResourceExists(outputDir)) {
await fs.mkdir(outputDir, {
@@ -81,10 +27,10 @@ const runProductionBuild = async (compilation) => {
});
}
- if (prerender || prerenderPlugin.prerender) {
- // start any servers if needed
+ if (shouldPrerender || (activeContent && shouldPrerender)) {
+ // start any of the user's server plugins if needed
const servers = [...compilation.config.plugins.filter((plugin) => {
- return plugin.type === 'server';
+ return plugin.type === 'server' && !plugin.isGreenwoodDefaultPlugin;
}).map((plugin) => {
const provider = plugin.provider(compilation);
@@ -95,6 +41,16 @@ const runProductionBuild = async (compilation) => {
return provider;
})];
+ if (activeContent) {
+ (await getDevServer({
+ ...compilation,
+ // prune for the content as data plugin and start the dev server with only that plugin enabled
+ plugins: [plugins.find(plugin => plugin.name === 'plugin-active-content')]
+ })).listen(compilation.config.devServer.port, () => {
+ console.info('Initializing active content...');
+ });
+ }
+
await Promise.all(servers.map(async (server) => {
await server.start();
@@ -102,13 +58,11 @@ const runProductionBuild = async (compilation) => {
}));
if (prerenderPlugin.executeModuleUrl) {
- await trackResourcesForRoutes(compilation);
await preRenderCompilationWorker(compilation, prerenderPlugin);
} else {
await preRenderCompilationCustom(compilation, prerenderPlugin);
}
} else {
- await trackResourcesForRoutes(compilation);
await staticRenderCompilation(compilation);
}
diff --git a/packages/cli/src/config/rollup.config.js b/packages/cli/src/config/rollup.config.js
index dc0d582d6..b0bad6bfd 100644
--- a/packages/cli/src/config/rollup.config.js
+++ b/packages/cli/src/config/rollup.config.js
@@ -1,3 +1,4 @@
+/* eslint-disable complexity, max-depth */
import fs from 'fs';
import path from 'path';
import { checkResourceExists, normalizePathnameForWindows } from '../lib/resource-utils.js';
@@ -6,31 +7,16 @@ import commonjs from '@rollup/plugin-commonjs';
import * as walk from 'acorn-walk';
// https://github.com/rollup/rollup/issues/2121
+// would be nice to get rid of this
function cleanRollupId(id) {
- return id.replace('\x00', '');
+ return id.replace('\x00', '').replace('?commonjs-proxy', '');
}
-// specifically to handle escodegen and other node modules
-// using require for package.json or other json files
-// https://github.com/estools/escodegen/issues/455
-function greenwoodJsonLoader() {
- return {
- name: 'greenwood-json-loader',
- async load(id) {
- const idUrl = new URL(`file://${cleanRollupId(id)}`);
- const extension = idUrl.pathname.split('.').pop();
-
- if (extension === 'json') {
- const json = JSON.parse(await fs.promises.readFile(idUrl, 'utf-8'));
- const contents = `export default ${JSON.stringify(json)}`;
+// ConstructableStylesheets, JSON Modules
+const externalizedResources = ['css', 'json'];
- return contents;
- }
- }
- };
-}
-
-function greenwoodResourceLoader (compilation) {
+function greenwoodResourceLoader (compilation, browser = false) {
+ const { importAttributes } = compilation.config?.polyfills;
const resourcePlugins = compilation.config.plugins.filter((plugin) => {
return plugin.type === 'resource';
}).map((plugin) => {
@@ -39,39 +25,78 @@ function greenwoodResourceLoader (compilation) {
return {
name: 'greenwood-resource-loader',
- async resolveId(id) {
- const normalizedId = cleanRollupId(id); // idUrl.pathname;
- const { projectDirectory, userWorkspace } = compilation.context;
-
- if (normalizedId.startsWith('.') && !normalizedId.startsWith(projectDirectory.pathname)) {
+ async resolveId(id, importer) {
+ const normalizedId = cleanRollupId(id);
+ const { userWorkspace } = compilation.context;
+
+ // check for non bare paths and resolve them to the user's workspace
+ // or Greenwood's scratch dir, like when bundling inline `;
+ })
+ ].join('\n');
+
+ const finalBody = pageLayoutContents
+ ? appBody.replace(/<\/page-outlet>/, pageBody)
+ : appBody;
+
+ mergedLayoutContents = `
+ ${mergedHtml}
+
+ ${title}
+ ${mergedMeta}
+ ${mergedLinks}
+ ${mergedStyles}
+ ${mergedScripts}
+
+
+ ${finalBody}
+
+