diff --git a/.core/babel.config.js b/.core/babel.config.js index cfccf025..baef3bd6 100644 --- a/.core/babel.config.js +++ b/.core/babel.config.js @@ -32,7 +32,7 @@ if (semver.satisfies(semver.coerce(babelCoreVersion), '^7.4.0')) { } } -require('./reactium.log'); +require('./reactium.log.cjs'); global.ReactiumBabel = ReactiumBabel; diff --git a/.core/boot-hooks.js b/.core/boot-hooks.mjs similarity index 52% rename from .core/boot-hooks.js rename to .core/boot-hooks.mjs index d33db102..79000b19 100644 --- a/.core/boot-hooks.js +++ b/.core/boot-hooks.mjs @@ -1,31 +1,43 @@ -import path from 'path'; import _ from 'underscore'; -const globby = require('./globby-patch').sync; +import path from 'node:path'; +import globbyPatched from './globby-patch.js' +import { dirname } from '@atomic-reactor/dirname'; + +const __dirname = dirname(import.meta.url); +const globby = globbyPatched.sync; global.rootPath = path.resolve(__dirname, '..'); -module.exports = async () => { +export default async () => { // include boot DDD artifacts if (!global.bootHooks) { global.bootHooks = globby([ `${rootPath}/.core/**/reactium-boot.js`, + `${rootPath}/.core/**/reactium-boot.mjs`, + `${rootPath}/.core/**/reactium-boot.cjs`, `${rootPath}/src/**/reactium-boot.js`, + `${rootPath}/src/**/reactium-boot.mjs`, + `${rootPath}/src/**/reactium-boot.cjs`, `${rootPath}/reactium_modules/**/reactium-boot.js`, + `${rootPath}/reactium_modules/**/reactium-boot.mjs`, + `${rootPath}/reactium_modules/**/reactium-boot.cjs`, `${rootPath}/node_modules/**/reactium-plugin/**/reactium-boot.js`, + `${rootPath}/node_modules/**/reactium-plugin/**/reactium-boot.mjs`, + `${rootPath}/node_modules/**/reactium-plugin/**/reactium-boot.cjs`, ]); } if (!global.bootHookLoaded) { DEBUG('Loading boot hooks.'); global.bootHookLoaded = []; - global.bootHooks.map(item => { + for (const item of global.bootHooks) { if (!bootHookLoaded.includes(item)) { const p = path.normalize(item); - require(p); + await import(p); bootHookLoaded.push(item); } - }); - + } + ReactiumBoot.Hook.runSync('sdk-init', ReactiumBoot); await ReactiumBoot.Hook.run('sdk-init', ReactiumBoot); } else { diff --git a/.core/gulp.bootup.js b/.core/gulp.bootup.js index 280f0474..48bf17dd 100644 --- a/.core/gulp.bootup.js +++ b/.core/gulp.bootup.js @@ -8,7 +8,7 @@ const config = require('./gulp.config'); const webpackConfig = require('./webpack.config')(config); const chalk = require('chalk'); -require('./reactium.log'); +require('./reactium.log.cjs'); global.ReactiumGulp = ReactiumGulp; diff --git a/.core/gulp.tasks.js b/.core/gulp.tasks.js index 1732ab74..59102381 100644 --- a/.core/gulp.tasks.js +++ b/.core/gulp.tasks.js @@ -228,9 +228,7 @@ const reactium = (gulp, config, webpackConfig) => { crossEnvBin, 'NODE_ENV=development', 'nodemon', - './.core/index.js', - '--exec', - 'babel-node', + './.core/index.mjs', ], done, { stdin: 'inherit' }, diff --git a/.core/index.js b/.core/index.mjs similarity index 90% rename from .core/index.js rename to .core/index.mjs index aee03a61..f892d303 100644 --- a/.core/index.js +++ b/.core/index.mjs @@ -8,16 +8,19 @@ import bodyParser from 'body-parser'; import cookieParser from 'cookie-parser'; import cookieSession from 'cookie-session'; import morgan from 'morgan'; -import path from 'path'; -import fs from 'fs'; import op from 'object-path'; import _ from 'underscore'; import staticGzip from 'express-static-gzip'; import chalk from 'chalk'; +import globals from './server-globals.mjs'; +import path from 'node:path'; +import fs from 'fs-extra'; +import globbyPatched from './globby-patch.js'; +import router from './server/router.mjs'; +import { dirname } from '@atomic-reactor/dirname'; -const globby = require('./globby-patch').sync; - -const globals = require('./server-globals'); +const __dirname = dirname(import.meta.url); +const globby = globbyPatched.sync; global.rootPath = path.resolve(__dirname, '..'); @@ -101,7 +104,7 @@ const registeredMiddleware = async () => { const reactiumModules = Object.keys( op.get( - require(path.resolve(process.cwd(), 'package.json')), + fs.readJsonSync(path.resolve(process.cwd(), 'package.json')), 'reactiumDependencies', {}, ), @@ -170,12 +173,12 @@ const registeredMiddleware = async () => { // default route handler ReactiumBoot.Server.Middleware.register('router', { name: 'router', - use: require('./server/router').default, + use: router, order: Enums.priority.neutral, }); }; -const registeredDevMiddleware = () => { +const registeredDevMiddleware = async () => { const { Enums } = ReactiumBoot; // set app variables @@ -183,11 +186,16 @@ const registeredDevMiddleware = () => { // development mode if (process.env.NODE_ENV === 'development') { - const webpack = require('webpack'); - const gulpConfig = require('./gulp.config'); - const webpackConfig = require('./webpack.config')(gulpConfig); - const wpMiddlware = require('webpack-dev-middleware'); - const wpHotMiddlware = require('webpack-hot-middleware'); + const { default: webpack } = await import('webpack'); + const { default: gulpConfig } = await import('./gulp.config.js'); + const { default: webpackConfigFactory } = await import( + './webpack.config.js' + ); + const webpackConfig = webpackConfigFactory(gulpConfig); + const { default: wpMiddleware } = await import('webpack-dev-middleware'); + const { default: wpHotMiddleware } = await import( + 'webpack-hot-middleware' + ); const publicPath = `http://localhost:${PORT}/`; // local development overrides for webpack config @@ -202,7 +210,7 @@ const registeredDevMiddleware = () => { ReactiumBoot.Server.Middleware.register('webpack', { name: 'webpack', - use: wpMiddlware(compiler, { + use: wpMiddleware(compiler, { serverSideRender: true, publicPath, }), @@ -211,7 +219,7 @@ const registeredDevMiddleware = () => { ReactiumBoot.Server.Middleware.register('hmr', { name: 'hmr', - use: wpHotMiddlware(compiler, { + use: wpHotMiddleware(compiler, { reload: true, }), order: Enums.priority.high, @@ -317,7 +325,7 @@ const startServer = async () => { }); if (process.env.REACTIUM_TLS_MODE === 'on') { - const spdy = require('spdy'); + const spdy = await import('spdy'); const options = { key: fs.readFileSync( op.get( diff --git a/.core/reactium.log.js b/.core/reactium.log.cjs similarity index 100% rename from .core/reactium.log.js rename to .core/reactium.log.cjs diff --git a/.core/sdk/reactium-boot.js b/.core/sdk/reactium-boot.js deleted file mode 100644 index e9417bb9..00000000 --- a/.core/sdk/reactium-boot.js +++ /dev/null @@ -1,22 +0,0 @@ -const Enums = ReactiumBoot.Enums; -Enums.priority.core = Enums.priority.highest - 1000; - -ReactiumBoot.Hook.register( - 'sdk-init', - async () => { - const { default: Routing } = await import('./routing'); - ReactiumBoot.Routing = Routing; - }, - Enums.priority.core, - 'REACTIUM_CORE_Routing', -); - -ReactiumBoot.Hook.register( - 'sdk-init', - async () => { - const { default: i18n } = await import('./i18n'); - ReactiumBoot.i18n = i18n; - }, - Enums.priority.core, - 'REACTIUM_CORE_i18n', -); diff --git a/.core/server-globals.js b/.core/server-globals.mjs similarity index 71% rename from .core/server-globals.js rename to .core/server-globals.mjs index a9bb0a63..f0954303 100644 --- a/.core/server-globals.js +++ b/.core/server-globals.mjs @@ -1,14 +1,17 @@ -import path from 'path'; import op from 'object-path'; import _ from 'underscore'; -import reactiumBootHooks from './boot-hooks'; +import reactiumBootHooks from './boot-hooks.mjs'; +import path from 'node:path'; +import { dirname } from '@atomic-reactor/dirname'; +import ReactiumBoot from '@atomic-reactor/reactium-sdk-core'; + +global.ReactiumBoot = ReactiumBoot; + +const __dirname = dirname(import.meta.url); global.rootPath = path.resolve(__dirname, '..'); -module.exports = async () => { - const ReactiumBoot = (await import('@atomic-reactor/reactium-sdk-core')) - .default; - global.ReactiumBoot = ReactiumBoot; +export default async () => { global.defines = {}; const defaultPort = 3030; @@ -35,7 +38,7 @@ module.exports = async () => { global.TLS_PORT = op.get(process.env, 'TLS_PORT', 3443); - require('./reactium.log'); + await import('./reactium.log.cjs'); await reactiumBootHooks(); }; diff --git a/.core/server/renderer/index.js b/.core/server/renderer/index.mjs similarity index 70% rename from .core/server/renderer/index.js rename to .core/server/renderer/index.mjs index 16f5f55b..3639ed8b 100644 --- a/.core/server/renderer/index.js +++ b/.core/server/renderer/index.mjs @@ -1,209 +1,216 @@ -const globby = require('globby'); -const path = require('path'); -const fs = require('fs'); -const semver = require('semver'); -const op = require('object-path'); -const _ = require('underscore'); -const serialize = require('serialize-javascript'); +import globby from 'globby'; +import path from 'path'; +import fs from 'fs'; +import semver from 'semver'; +import op from 'object-path'; +import _ from 'underscore'; +import serialize from 'serialize-javascript'; +import reactiumConfig from '../../reactium-config.js'; const normalizeAssets = assets => _.flatten([assets]); -ReactiumBoot.Hook.registerSync( - 'Server.AppStyleSheets', - (req, AppStyleSheets) => { - const theme = op.get( - req, - 'query.theme', - process.env.DEFAULT_THEME || 'style', - ); - - const defaultStylesheet = `${theme}.css`; - - let styles = []; - let publicDir = - process.env.PUBLIC_DIRECTORY || - path.resolve(process.cwd(), 'public'); - let styleDir = path.normalize(path.join(publicDir, '/assets/style')); - - const corePath = path - .normalize(path.join(styleDir, 'core.css')) - .split(publicDir) - .join(''); - - const when = (req, itemPath) => { - const [url] = req.originalUrl.split('?'); - - const includes = [defaultStylesheet]; - ReactiumBoot.Hook.runSync( - 'Server.AppStyleSheets.includes', - includes, +(async () => { + ReactiumBoot.Hook.registerSync( + 'Server.AppStyleSheets', + (req, AppStyleSheets) => { + const theme = op.get( + req, + 'query.theme', + process.env.DEFAULT_THEME || 'style', ); - const excludes = ['core.css', 'toolkit.css']; - ReactiumBoot.Hook.runSync( - 'Server.AppStyleSheets.excludes', - excludes, - ); + const defaultStylesheet = `${theme}.css`; - const included = Boolean( - includes.find(search => itemPath.indexOf(search) >= 0), - ); - const excluded = Boolean( - excludes.find(search => itemPath.indexOf(search) >= 0), + let styles = []; + let publicDir = + process.env.PUBLIC_DIRECTORY || + path.resolve(process.cwd(), 'public'); + let styleDir = path.normalize( + path.join(publicDir, '/assets/style'), ); - return included && !excluded; - }; + const corePath = path + .normalize(path.join(styleDir, 'core.css')) + .split(publicDir) + .join(''); - fs.readdirSync(styleDir).forEach(item => { - const itemPath = path.normalize(path.join(styleDir, item)); - const cssPath = itemPath.split(publicDir).join(''); + const when = (req, itemPath) => { + const [url] = req.originalUrl.split('?'); + + const includes = [defaultStylesheet]; + ReactiumBoot.Hook.runSync( + 'Server.AppStyleSheets.includes', + includes, + ); - AppStyleSheets.register(path.basename(itemPath), { - path: itemPath.split(publicDir).join(''), - when, + const excludes = ['core.css', 'toolkit.css']; + ReactiumBoot.Hook.runSync( + 'Server.AppStyleSheets.excludes', + excludes, + ); + + const included = Boolean( + includes.find(search => itemPath.indexOf(search) >= 0), + ); + const excluded = Boolean( + excludes.find(search => itemPath.indexOf(search) >= 0), + ); + + return included && !excluded; + }; + + fs.readdirSync(styleDir).forEach(item => { + const itemPath = path.normalize(path.join(styleDir, item)); + const cssPath = itemPath.split(publicDir).join(''); + + AppStyleSheets.register(path.basename(itemPath), { + path: itemPath.split(publicDir).join(''), + when, + }); }); - }); - }, - ReactiumBoot.Enums.priority.highest, - 'SERVER-APP-STYLESHEETS-CORE', -); - -ReactiumBoot.Hook.registerSync( - 'Server.AppScripts', - (req, AppScripts, res) => { - // Webpack assets - if (process.env.NODE_ENV === 'development') { - const { stats: context } = res.locals.webpack.devMiddleware; - const stats = context.toJson(); - _.pluck(stats.namedChunkGroups.main.assets, 'name').forEach( - path => { - AppScripts.register(path, { - path: `/${path}`, + }, + ReactiumBoot.Enums.priority.highest, + 'SERVER-APP-STYLESHEETS-CORE', + ); + + ReactiumBoot.Hook.registerSync( + 'Server.AppScripts', + (req, AppScripts, res) => { + // Webpack assets + if (process.env.NODE_ENV === 'development') { + const { stats: context } = res.locals.webpack.devMiddleware; + const stats = context.toJson(); + _.pluck(stats.namedChunkGroups.main.assets, 'name').forEach( + path => { + AppScripts.register(path, { + path: `/${path}`, + order: ReactiumBoot.Enums.priority.highest, + footer: true, + }); + }, + ); + + return; + } + + try { + const webpackAssets = JSON.parse( + fs.readFileSync( + path.resolve( + rootPath, + 'src/app/server/webpack-manifest.json', + ), + ), + ); + + ReactiumBoot.Hook.runSync( + 'webpack-server-assets', + webpackAssets, + ); + webpackAssets.forEach(asset => + AppScripts.register(asset, { + path: `${global.resourceBaseUrl}${asset}`, order: ReactiumBoot.Enums.priority.highest, footer: true, - }); - }, - ); + }), + ); + } catch (error) { + console.error( + 'build/src/app/server/webpack-manifest.json not found or invalid JSON', + error, + ); + process.exit(1); + } + }, + ReactiumBoot.Enums.priority.highest, + 'SERVER-APP-SCRIPTS-CORE', + ); - return; - } + ReactiumBoot.Hook.registerSync( + 'Server.AppHeaders', + (req, AppHeaders, res) => { + AppHeaders.register('shortcut', { + header: + '', + order: ReactiumBoot.Enums.priority.highest, + }); + AppHeaders.register('favicon', { + header: + '', + order: ReactiumBoot.Enums.priority.highest, + }); + AppHeaders.register('viewport', { + header: + '', + order: ReactiumBoot.Enums.priority.highest, + }); + AppHeaders.register('charset', { + header: '', + order: ReactiumBoot.Enums.priority.highest, + }); + }, + ReactiumBoot.Enums.priority.highest, + 'SERVER-APP-HEADERS-CORE', + ); - try { - const webpackAssets = JSON.parse( - fs.readFileSync( - path.resolve( - rootPath, - 'src/app/server/webpack-manifest.json', - ), - ), - ); + ReactiumBoot.Hook.registerSync( + 'Server.AppBindings', + (req, AppBindings) => { + AppBindings.register('router', { + template: () => { + const binding = `
`; + return binding; + }, + requestParams: ['content'], + }); + }, + ReactiumBoot.Enums.priority.highest, + 'SERVER-APP-BINDINGS-CORE', + ); - ReactiumBoot.Hook.runSync('webpack-server-assets', webpackAssets); - webpackAssets.forEach(asset => - AppScripts.register(asset, { - path: `${global.resourceBaseUrl}${asset}`, - order: ReactiumBoot.Enums.priority.highest, - footer: true, - }), - ); - } catch (error) { - console.error( - 'build/src/app/server/webpack-manifest.json not found or invalid JSON', - error, - ); - process.exit(1); + const sanitizeTemplateVersion = version => { + if (semver.valid(version)) { + return version; } - }, - ReactiumBoot.Enums.priority.highest, - 'SERVER-APP-SCRIPTS-CORE', -); - -ReactiumBoot.Hook.registerSync( - 'Server.AppHeaders', - (req, AppHeaders, res) => { - AppHeaders.register('shortcut', { - header: - '', - order: ReactiumBoot.Enums.priority.highest, - }); - AppHeaders.register('favicon', { - header: - '', - order: ReactiumBoot.Enums.priority.highest, - }); - AppHeaders.register('viewport', { - header: - '', - order: ReactiumBoot.Enums.priority.highest, - }); - AppHeaders.register('charset', { - header: '', - order: ReactiumBoot.Enums.priority.highest, - }); - }, - ReactiumBoot.Enums.priority.highest, - 'SERVER-APP-HEADERS-CORE', -); - -ReactiumBoot.Hook.registerSync( - 'Server.AppBindings', - (req, AppBindings) => { - AppBindings.register('router', { - template: () => { - const binding = ` `; - return binding; - }, - requestParams: ['content'], - }); - }, - ReactiumBoot.Enums.priority.highest, - 'SERVER-APP-BINDINGS-CORE', -); - -const sanitizeTemplateVersion = version => { - if (semver.valid(version)) { - return version; - } - return semver.coerce(version); -}; - -ReactiumBoot.Hook.registerSync( - 'Server.beforeApp', - req => { - const { - semver: coreSemver, - } = require(`${rootPath}/.core/reactium-config`); - - if (fs.existsSync(`${rootPath}/src/app/server/template/feo.js`)) { - let localTemplate = require(`${rootPath}/src/app/server/template/feo`); - let templateVersion = sanitizeTemplateVersion( - localTemplate.version, - ); + return semver.coerce(version); + }; + + ReactiumBoot.Hook.register( + 'Server.beforeApp', + async req => { + if (fs.existsSync(`${rootPath}/src/app/server/template/feo.js`)) { + let { default: localTemplate } = await import( + `${rootPath}/src/app/server/template/feo.js` + ); - // Check to see if local template should be compatible with core - if (semver.satisfies(templateVersion, coreSemver)) { - req.template = localTemplate.template; - } else { - console.warn( - `${rootPath}/src/app/server/template/feo.js is out of date, and will not be used. Use 'arcli server template' command to update.`, + let templateVersion = sanitizeTemplateVersion( + localTemplate.version, ); + + // Check to see if local template should be compatible with core + if (semver.satisfies(templateVersion, reactiumConfig.semver)) { + req.template = localTemplate.template; + } else { + console.warn( + `${rootPath}/src/app/server/template/feo.js is out of date, and will not be used. Use 'arcli server template' command to update.`, + ); + } } - } - }, - ReactiumBoot.Enums.priority.highest, - 'SERVER-BEFORE-APP-CORE-TEMPLATES', -); - -ReactiumBoot.Hook.registerSync('Server.AppGlobals', (req, AppGlobals) => { - AppGlobals.register('resourceBaseUrl', { - name: 'resourceBaseUrl', - value: - process.env.NODE_ENV === 'development' - ? '/' - : process.env.WEBPACK_RESOURCE_BASE || '/assets/js/', + }, + ReactiumBoot.Enums.priority.highest, + 'SERVER-BEFORE-APP-CORE-TEMPLATES', + ); + + ReactiumBoot.Hook.registerSync('Server.AppGlobals', (req, AppGlobals) => { + AppGlobals.register('resourceBaseUrl', { + name: 'resourceBaseUrl', + value: + process.env.NODE_ENV === 'development' + ? '/' + : process.env.WEBPACK_RESOURCE_BASE || '/assets/js/', + }); }); -}); +})(); export const renderAppBindings = req => { let bindingsMarkup = ''; @@ -275,7 +282,7 @@ export default async (req, res, context) => { req.headTags = ''; req.appBindings = ''; - const coreTemplate = require(`../template/feo`); + const { default: coreTemplate } = await import(`../template/feo.js`); req.template = coreTemplate.template; /** @@ -625,6 +632,7 @@ ga('send', 'pageview'); */ ReactiumBoot.Hook.runSync('Server.afterApp', req, Server); await ReactiumBoot.Hook.run('Server.afterApp', req, Server); + const { default: feo } = await import('./feo.js'); - return require(`./feo`)(req, res, context); + return feo(req, res, context); }; diff --git a/.core/server/router.js b/.core/server/router.mjs similarity index 98% rename from .core/server/router.js rename to .core/server/router.mjs index 20247f41..570ce1a2 100644 --- a/.core/server/router.js +++ b/.core/server/router.mjs @@ -1,5 +1,5 @@ import express from 'express'; -import renderer from './renderer'; +import renderer from './renderer/index.mjs'; import fs from 'fs'; import path from 'path'; import httpAuth from 'http-auth'; diff --git a/.core/server/template/feo.js b/.core/server/template/feo.js index 01ad296a..a77adda9 100644 --- a/.core/server/template/feo.js +++ b/.core/server/template/feo.js @@ -1,4 +1,4 @@ -import serialize from 'serialize-javascript'; +const serialize = require('serialize-javascript'); module.exports = { version: '%TEMPLATE_VERSION%', diff --git a/package-lock.json b/package-lock.json index 0dc619fc..ddef7101 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,7 @@ "version": "5.0.0-alpha-3", "license": "MIT", "dependencies": { + "@atomic-reactor/dirname": "^1.0.2", "@atomic-reactor/reactium-api": "file:reactium_modules/@atomic-reactor/reactium-api/_npm", "@atomic-reactor/reactium-capability": "file:reactium_modules/@atomic-reactor/reactium-capability/_npm", "@atomic-reactor/reactium-role": "file:reactium_modules/@atomic-reactor/reactium-role/_npm", @@ -190,6 +191,11 @@ "node": ">=0.10.0" } }, + "node_modules/@atomic-reactor/dirname": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@atomic-reactor/dirname/-/dirname-1.0.2.tgz", + "integrity": "sha512-VwhCkROeMaZIsZV/iiCOIxRjQ1Uikxo5n3YOSpZExQkECYmadzjtNV4phzk4lt6NKVTgnTCFPUdZAAk+RjK7kA==" + }, "node_modules/@atomic-reactor/gulp-run": { "version": "1.8.0", "resolved": "https://registry.npmjs.org/@atomic-reactor/gulp-run/-/gulp-run-1.8.0.tgz", diff --git a/package.json b/package.json index 1690b89f..d10e6b4d 100644 --- a/package.json +++ b/package.json @@ -4,12 +4,8 @@ "description": "A framework for creating React + Redux apps using the domain driven design (DDD) paradigm.", "main": "index.js", "scripts": { - "start": "node build/.core/index.js", - "build": "run-s build:*", - "build:gulp": "cross-env NODE_ENV=production gulp", - "build:babel-core": "cross-env NODE_ENV=production babel .core --out-dir build/.core", - "build:babel-reactium_modules": "cross-env NODE_ENV=production babel reactium_modules --out-dir build/reactium_modules", - "build:babel-src": "cross-env NODE_ENV=production babel src --out-dir build/src --copy-files", + "start": "node .core/index.mjs", + "build": "cross-env NODE_ENV=production gulp", "heroku-prebuild": "npx reactium install", "static": "npm-run-all build:* && gulp static", "local": "gulp local", @@ -40,6 +36,7 @@ "parse/node": false }, "dependencies": { + "@atomic-reactor/dirname": "^1.0.2", "@atomic-reactor/reactium-api": "file:reactium_modules/@atomic-reactor/reactium-api/_npm", "@atomic-reactor/reactium-capability": "file:reactium_modules/@atomic-reactor/reactium-capability/_npm", "@atomic-reactor/reactium-role": "file:reactium_modules/@atomic-reactor/reactium-role/_npm", @@ -178,4 +175,4 @@ "@atomic-reactor/reactium-setting": "5.0.1", "@atomic-reactor/reactium-svg": "0.0.3" } -} \ No newline at end of file +}