From e369fe98324273318dc31aaf0e9f98e22428ce71 Mon Sep 17 00:00:00 2001 From: Ricardo Devis Agullo Date: Tue, 28 Mar 2023 21:20:12 +0200 Subject: [PATCH 1/2] update to use vite --- README.md | 148 ++++------- .../oc-template-react-compiler/.prettierrc.js | 1 + .../oc-template-react-compiler/globals.d.ts | 47 ++++ .../oc-template-react-compiler/lib/compile.js | 39 ++- .../lib/compileServer.js | 137 +++++----- .../lib/compileView.js | 233 +++++++++--------- .../lib/cssModulesPlugin.js | 81 ++++++ .../lib/higherOrderServerTemplate.js | 18 +- .../lib/oc-app.d.ts | 72 ++++++ .../lib/reactOCProviderTemplate.js | 68 ++--- .../font-family-unicode-parser.js | 25 -- .../oc-webpack/index.js | 9 - .../oc-webpack/lib/compiler/index.js | 42 ---- .../lib/configurator/client/index.js | 120 --------- .../lib/configurator/createExcludeRegex.js | 6 - .../oc-webpack/lib/configurator/index.js | 9 - .../lib/configurator/server/index.js | 93 ------- .../lib/verifyConfig.js | 212 ++++++++++++++++ .../lib/viewTemplate.js | 41 ++- .../oc-template-react-compiler/package.json | 41 ++- .../scaffold/src/.eslintrc.json | 3 + .../scaffold/src/app.js | 11 - .../scaffold/src/package.json | 40 ++- .../scaffold/src/public/logo.png | Bin 0 -> 57405 bytes .../scaffold/src/server.js | 4 - .../scaffold/src/src/App.test.tsx | 29 +++ .../scaffold/src/src/App.tsx | 51 ++++ .../scaffold/src/src/oc-app.d.ts | 2 + .../scaffold/src/src/server.ts | 34 +++ .../scaffold/src/src/setupTests.js | 1 + .../scaffold/src/src/styles.css | 30 +++ .../scaffold/src/src/types.ts | 15 ++ .../scaffold/src/styles.css | 4 - .../scaffold/src/tsconfig.json | 26 ++ .../scaffold/src/vite.config.ts | 9 + .../utils/.eslintrc.js | 13 + .../oc-template-react-compiler/utils/index.js | 4 - .../utils/useData.tsx | 38 +++ .../utils/withDataProvider.js | 21 -- .../utils/withSettingProvider.js | 21 -- 40 files changed, 1017 insertions(+), 781 deletions(-) create mode 100644 packages/oc-template-react-compiler/.prettierrc.js create mode 100644 packages/oc-template-react-compiler/globals.d.ts create mode 100644 packages/oc-template-react-compiler/lib/cssModulesPlugin.js create mode 100644 packages/oc-template-react-compiler/lib/oc-app.d.ts delete mode 100644 packages/oc-template-react-compiler/lib/to-abstract-base-template-utils/font-family-unicode-parser.js delete mode 100644 packages/oc-template-react-compiler/lib/to-abstract-base-template-utils/oc-webpack/index.js delete mode 100644 packages/oc-template-react-compiler/lib/to-abstract-base-template-utils/oc-webpack/lib/compiler/index.js delete mode 100644 packages/oc-template-react-compiler/lib/to-abstract-base-template-utils/oc-webpack/lib/configurator/client/index.js delete mode 100644 packages/oc-template-react-compiler/lib/to-abstract-base-template-utils/oc-webpack/lib/configurator/createExcludeRegex.js delete mode 100644 packages/oc-template-react-compiler/lib/to-abstract-base-template-utils/oc-webpack/lib/configurator/index.js delete mode 100644 packages/oc-template-react-compiler/lib/to-abstract-base-template-utils/oc-webpack/lib/configurator/server/index.js create mode 100644 packages/oc-template-react-compiler/lib/verifyConfig.js create mode 100644 packages/oc-template-react-compiler/scaffold/src/.eslintrc.json delete mode 100644 packages/oc-template-react-compiler/scaffold/src/app.js create mode 100644 packages/oc-template-react-compiler/scaffold/src/public/logo.png delete mode 100644 packages/oc-template-react-compiler/scaffold/src/server.js create mode 100644 packages/oc-template-react-compiler/scaffold/src/src/App.test.tsx create mode 100644 packages/oc-template-react-compiler/scaffold/src/src/App.tsx create mode 100644 packages/oc-template-react-compiler/scaffold/src/src/oc-app.d.ts create mode 100644 packages/oc-template-react-compiler/scaffold/src/src/server.ts create mode 100644 packages/oc-template-react-compiler/scaffold/src/src/setupTests.js create mode 100644 packages/oc-template-react-compiler/scaffold/src/src/styles.css create mode 100644 packages/oc-template-react-compiler/scaffold/src/src/types.ts delete mode 100644 packages/oc-template-react-compiler/scaffold/src/styles.css create mode 100644 packages/oc-template-react-compiler/scaffold/src/tsconfig.json create mode 100644 packages/oc-template-react-compiler/scaffold/src/vite.config.ts create mode 100644 packages/oc-template-react-compiler/utils/.eslintrc.js delete mode 100644 packages/oc-template-react-compiler/utils/index.js create mode 100644 packages/oc-template-react-compiler/utils/useData.tsx delete mode 100644 packages/oc-template-react-compiler/utils/withDataProvider.js delete mode 100644 packages/oc-template-react-compiler/utils/withSettingProvider.js diff --git a/README.md b/README.md index 487fe64..5c7c739 100644 --- a/README.md +++ b/README.md @@ -1,158 +1,108 @@ -oc-template-react -================= +# oc-template-typescript-react -react-templates & utilities for the [OpenComponents](https://github.com/opentable/oc) template system +React-templates with TypeScript support & utilities for the [OpenComponents](https://github.com/opentable/oc) template system -*** +Based on Nick Balestra's work on [oc-template-react](https://github.com/opencomponents/oc-template-react) -Module for handling React templates in OC - -[![codecov](https://codecov.io/gh/opencomponents/oc-template-react/branch/master/graph/badge.svg)](https://codecov.io/gh/opencomponents/oc-template-react) -[![Known Vulnerabilities](https://snyk.io/test/github/opencomponents/oc-template-react/badge.svg)](https://snyk.io/test/github/opencomponents/oc-template-react) -[![npm version](https://badge.fury.io/js/oc-template-react.svg)](http://badge.fury.io/js/oc-template-react) - -| Node8 | Node9 | Node10 | -|-------------------|-------------------|-------------------| -| [![Node8][1]][4] | [![Node9][2]][4] | [![Node10][3]][4] | - -[1]: https://travis-matrix-badges.herokuapp.com/repos/opencomponents/oc-template-react/branches/master/1 -[2]: https://travis-matrix-badges.herokuapp.com/repos/opencomponents/oc-template-react/branches/master/2 -[3]: https://travis-matrix-badges.herokuapp.com/repos/opencomponents/oc-template-react/branches/master/3 -[4]: https://travis-ci.org/opencomponents/oc-template-react +--- ## Packages included in this repo -| Name | Version | -|--------|-------| -| [`oc-template-react`](/packages/oc-template-react) | [![npm version](https://badge.fury.io/js/oc-template-react.svg)](http://badge.fury.io/js/oc-template-react) | -| [`oc-template-react-compiler`](/packages/oc-template-react-compiler) | [![npm version](https://badge.fury.io/js/oc-template-react-compiler.svg)](http://badge.fury.io/js/oc-template-react-compiler) | -| [`oc-react-component-wrapper`](/packages/oc-react-component-wrapper) | [![npm version](https://badge.fury.io/js/oc-react-component-wrapper.svg)](http://badge.fury.io/js/oc-react-component-wrapper) | - +| Name | Version | +| ------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------- | +| [`oc-template-typescript-react`](/packages/oc-template-typescript-react) | [![npm version](https://badge.fury.io/js/oc-template-typescript-react.svg)](http://badge.fury.io/js/oc-template-typescript-react) | +| [`oc-template-typescript-react-compiler`](/packages/oc-template-typescript-react-compiler) | [![npm version](https://badge.fury.io/js/oc-template-typescript-react-compiler.svg)](http://badge.fury.io/js/oc-template-typescript-react-compiler) | ## Usage: Initialize a component with the oc-template-react and follow the CLI instructions ``` -$ oc init oc-template-react +$ npx oc init my-awesome-oc oc-template-typescript-react +$ cd my-awesome-oc +$ npm install ``` ## Extra info: + +### Linting + +Like in [Create React App](https://create-react-app.dev/docs/setting-up-your-editor/#displaying-lint-output-in-the-editor), linting will be done during the build, and you can extend it from .eslintrc.json, by setting the EXTEND_ESLINT environment variable to true. + +It can also be disabled completely by setting the `DISABLE_ESLINT_PLUGIN` environment variable to `true`. + ### package.json requirements -- `template.src` - the react App entry point - should export a react component as `default` -- `template.type` - `oc-template-react` -- required in `devDependencies` - `oc-template-react-compiler` -### package.json optional -- `template.externals` - override unpkg.com externals -``` - "externals": [ - { - "name": "Object.assign", - "global": [ - "Object", - "assign" - ], - "url": "https://mydomain.com/es6-object-assign@1.1.0/dist/object-assign-auto.min.js" - }, - { - "name": "prop-types", - "global": "PropTypes", - "url": "https://mydomain.com/prop-types@15.7.2/prop-types.min.js" - }, - { - "name": "react", - "global": "React", - "url": "https://mydomain.com/react@16.9.0/umd/react.production.min.js" - }, - { - "name": "react-dom", - "global": "ReactDOM", - "url": "https://mydomain.com/react-dom@16.9.0/umd/react-dom.production.min.js" - } - ] -``` + +- `template.src` - the react App entry point - should export a react component as `default` +- `template.type` - `oc-template-typescript-react` +- required in `devDependencies` - `oc-template-react-compiler`, `react`, `react-dom`, `@types/react` ### conventions + - `props = server()` - the viewModel generated by the server is automatically passed to the react application as props - The oc-client-browser is extended and will now expose all the available react-component at `oc.reactComponents[bundleHash]` - You can register an event handler within the [oc.events](https://github.com/opentable/oc/wiki/Browser-client#oceventsoneventname-callback) system for the the `oc:componentDidMount` event. The event will be fired immediately after the react app is mounted. - You can register an event handler within the [oc.events](https://github.com/opentable/oc/wiki/Browser-client#oceventsoneventname-callback) system for the the `oc:cssDidMount` event. The event will be fired immediately after the style tag will be added to the active DOM tree. ### externals + Externals are not bundled when packaging and publishing the component, for better size taking advantage of externals caching. OC will make sure to provide them at Server-Side & Client-Side rendering time for you. + - React - ReactDOM -- PropTypes - + ### features + - `Server Side Rendering` = server side rendering should work as for any other OpenComponent - `css-modules` are supported. +- `sass-modules` are supported (you need to install the `node-sass` dependency). - `post-css` is supported with the following plugins: - [postcss-import](https://github.com/postcss/postcss-import) - [postcss-extend](https://github.com/travco/postcss-extend) - [postcss-icss-values](https://github.com/css-modules/postcss-icss-values) - [autoprefixer](https://github.com/postcss/autoprefixer) -- `White list dependencies` to be inlcuded in the build process done by the compiler. To whitelist dependencies installed in the node_modules folder, add in the package.json of the component a `buildIncludes` list: - ```json - ... - oc : { - files: { - template: { - ... - buildIncludes: ['react-components-to-build'] - } - } - } - ``` - - ## Utils The compiler exposes some utilities that could be used by your React application: -### HOCs +### Hooks -#### withDataProvider +#### useData -An Higher order component that will make a `getData` function available via props. +A hook that will make a `getData` function available via props, plus +the initial data passed from the server to the component. ##### Usage: ```javascript -import { withDataProvider } from 'oc-template-react-compiler/utils'; - -const yourApp = props => { - // you can use props.getData here +import { useData } from 'oc-template-typescript-react-compiler/utils/useData'; + +const App = (props) => { + // getData and getSetting are always available + const { id, getData, getSetting } = useData<{ id: number }>(); + const staticPath = getSetting('staticPath'); + + return ( +
+

Id: {id}

+ Logo +
+ ) }; yourEnhancedApp = withDataProvider(yourApp); ``` -`getData` accept two arguments: `(params, callback) => callback(err, result)`. It will perform a post back request to the component endpoint with the specified query perams invoking the callback with the results. - -For more details, check the [`example app`](/acceptance-components/react-app/app.js) - -#### withSettingProvider - -An Higher order component that will make a `getSetting` function available via props. - -##### Usage: - -```javascript -import { withSettingProvider } from 'oc-template-react-compiler/utils'; - -const yourApp = props => { - // you can use props.getSetting here -}; - -yourEnhancedApp = withSettingProvider(yourApp); -``` +`getData` accept one argument: `(params) => Promise`. It will perform a post back request to the component endpoint with the specified query perams invoking the callback with the results. `getSetting` accept one argument: `settingName => settingValue`. It will return the value for the requested setting. Settings available at the moment: + - `getSetting('name')` : return the name of the OC component - `getSetting('version')` : return the version of the OC component - `getSetting('baseUrl')` : return the [baseUrl of the oc-registry](https://github.com/opentable/oc/wiki/The-server.js#context-properties) - `getSetting('staticPath')` : return the path to the [static assets](https://github.com/opentable/oc/wiki/The-server.js#add-static-resource-to-the-component) of the OC component + +For more details, check the [`example app`](/acceptance-components/react-app/app.js) diff --git a/packages/oc-template-react-compiler/.prettierrc.js b/packages/oc-template-react-compiler/.prettierrc.js new file mode 100644 index 0000000..7901b6a --- /dev/null +++ b/packages/oc-template-react-compiler/.prettierrc.js @@ -0,0 +1 @@ +module.exports = require('prettier-config-guestline'); diff --git a/packages/oc-template-react-compiler/globals.d.ts b/packages/oc-template-react-compiler/globals.d.ts new file mode 100644 index 0000000..997b19d --- /dev/null +++ b/packages/oc-template-react-compiler/globals.d.ts @@ -0,0 +1,47 @@ +declare module 'oc-generic-template-compiler' { + type Callback = (err: Error | null, data?: T) => void; + type CompiledViewInfo = any; + + type OcOptions = { + files: { + data: string; + template: { + src: string; + type: string; + }; + static: string[]; + }; + }; + + type Options = { + publishPath: string; + componentPath: string; + componentPackage: { + name: string; + version: string; + dependencies: Record; + oc: OcOptions; + }; + ocPackage: any; + minify: boolean; + verbose: boolean; + production: boolean; + }; + + type CompileServer = ( + options: Options & { compiledViewInfo: CompiledViewInfo }, + cb: Callback + ) => void; + type CompileView = (options: Options, cb: Callback) => void; + type CompileStatics = (options: Options, cb: Callback<'ok'>) => void; + type GetInfo = () => { + version: string; + }; + + export function createCompile(parameters: { + compileView: CompileView; + compileServer: CompileServer; + compileStatics: CompileStatics; + getInfo: GetInfo; + }): (options: Options, cb: Callback) => void; +} diff --git a/packages/oc-template-react-compiler/lib/compile.js b/packages/oc-template-react-compiler/lib/compile.js index d56a291..dbd210d 100644 --- a/packages/oc-template-react-compiler/lib/compile.js +++ b/packages/oc-template-react-compiler/lib/compile.js @@ -1,11 +1,21 @@ -"use strict"; +'use strict'; -const createCompile = require("oc-generic-template-compiler").createCompile; -const compileStatics = require("oc-statics-compiler"); -const getInfo = require("oc-template-react").getInfo; +const createCompile = require('oc-generic-template-compiler').createCompile; +const compileStatics = require('oc-statics-compiler'); +const getInfo = require('oc-template-typescript-react').getInfo; -const compileServer = require("./compileServer"); -const compileView = require("./compileView"); +const compileServer = require('./compileServer'); +const compileView = require('./compileView'); +const verifyTypeScriptSetup = require('./verifyConfig'); + +const compiler = createCompile({ + compileServer, + compileStatics, + compileView, + getInfo +}); + +const hasTsExtension = (file) => !!file.match(/\.tsx?$/); // OPTIONS // ======= @@ -18,9 +28,14 @@ const compileView = require("./compileView"); // verbose, // watch, // production -module.exports = createCompile({ - compileServer, - compileStatics, - compileView, - getInfo -}); +module.exports = function compile(options, callback) { + const viewFileName = options.componentPackage.oc.files.template.src; + const serverFileName = options.componentPackage.oc.files.data; + const usingTypescript = hasTsExtension(viewFileName) || hasTsExtension(serverFileName); + + if (usingTypescript) { + verifyTypeScriptSetup(options.componentPath); + } + + return compiler(options, callback); +}; diff --git a/packages/oc-template-react-compiler/lib/compileServer.js b/packages/oc-template-react-compiler/lib/compileServer.js index e103a05..5ebb97f 100644 --- a/packages/oc-template-react-compiler/lib/compileServer.js +++ b/packages/oc-template-react-compiler/lib/compileServer.js @@ -1,100 +1,75 @@ -"use strict"; +const vite = require('vite'); +const fs = require('fs-extra'); +const path = require('path'); +const { callbackify } = require('util'); +const coreModules = require('builtin-modules'); +const hashBuilder = require('oc-hash-builder'); +const higherOrderServerTemplate = require('./higherOrderServerTemplate'); -const async = require("async"); -const fs = require("fs-extra"); -const hashBuilder = require("oc-hash-builder"); -const MemoryFS = require("memory-fs"); -const path = require("path"); -const reactComponentWrapper = require("oc-react-component-wrapper"); +const nodeModuleMatcher = /^[a-z@][a-z\-/0-9.]+$/i; -const { - compiler, - configurator: { server: webpackConfigurator } -} = require("./to-abstract-base-template-utils/oc-webpack"); -const higherOrderServerTemplate = require("./higherOrderServerTemplate"); - -module.exports = (options, callback) => { +async function compileServer(options) { + const componentPath = options.componentPath; const serverFileName = options.componentPackage.oc.files.data; let serverPath = path.join(options.componentPath, serverFileName); - if (process.platform === "win32") { - serverPath = serverPath.split("\\").join("\\\\"); + if (process.platform === 'win32') { + serverPath = serverPath.split('\\').join('\\\\'); } - const publishFileName = options.publishFileName || "server.js"; + const publishFileName = options.publishFileName || 'server.js'; const publishPath = options.publishPath; - const stats = options.verbose ? "verbose" : "errors-only"; const dependencies = options.componentPackage.dependencies || {}; const componentName = options.componentPackage.name; const componentVersion = options.componentPackage.version; - const production = options.production; + const production = !!options.production; const higherOrderServerContent = higherOrderServerTemplate({ serverPath, componentName, - componentVersion, - bundleHashKey: options.compiledViewInfo.bundle.hashKey - }); - const tempFolder = path.join(serverPath, "../temp"); - const higherOrderServerPath = path.join( - tempFolder, - "__oc_higherOrderServer.js" - ); - - const config = webpackConfigurator({ - serverPath: higherOrderServerPath, - publishFileName, - dependencies, - stats, - production + componentVersion }); + const tempFolder = path.join(publishPath, 'temp'); + const higherOrderServerPath = path.join(tempFolder, '__oc_higherOrderServer.ts'); + const externals = [...Object.keys(dependencies), ...coreModules]; - async.waterfall( - [ - next => - fs.outputFile(higherOrderServerPath, higherOrderServerContent, next), - next => compiler(config, next), - (data, next) => { - const basePath = path.join(serverPath, "../temp/build"); - const memory = new MemoryFS(data); - const getCompiled = fileName => - memory.readFileSync(`${basePath}/${fileName}`, "UTF8"); - - return fs.ensureDir(publishPath, err => { - if (err) return next(err); - const result = { "server.js": getCompiled(config.output.filename) }; + try { + await fs.outputFile(higherOrderServerPath, higherOrderServerContent); - if (!production) { - try { - result["server.js.map"] = getCompiled( - `${config.output.filename}.map` - ); - } catch (e) { - // skip sourcemap if it doesn't exist + const result = await vite.build({ + appType: 'custom', + root: componentPath, + mode: production ? 'production' : 'development', + logLevel: options.verbose ? 'info' : 'silent', + build: { + lib: { entry: higherOrderServerPath, formats: ['cjs'] }, + write: false, + minify: production, + rollupOptions: { + external: (id) => { + if (nodeModuleMatcher.test(id)) { + if (!externals.includes(id)) { + throw new Error(`Missing dependencies from package.json => ${id}`); + } + return true; } + return false; } + } + } + }); + const out = Array.isArray(result) ? result[0] : result; + const bundle = out.output[0].code; + + await fs.ensureDir(publishPath); + await fs.writeFile(path.join(publishPath, publishFileName), bundle); + + return { + type: 'node.js', + hashKey: hashBuilder.fromString(bundle), + src: publishFileName + }; + } finally { + await fs.remove(tempFolder); + } +} - next(null, result); - }); - }, - (compiledFiles, next) => - async.eachOf( - compiledFiles, - (fileContent, fileName, next) => - fs.writeFile(path.join(publishPath, fileName), fileContent, next), - err => - next( - err, - err - ? null - : { - type: "node.js", - hashKey: hashBuilder.fromString( - compiledFiles[publishFileName] - ), - src: publishFileName - } - ) - ) - ], - (err, data) => fs.remove(tempFolder, err2 => callback(err, data)) - ); -}; +module.exports = callbackify(compileServer); diff --git a/packages/oc-template-react-compiler/lib/compileView.js b/packages/oc-template-react-compiler/lib/compileView.js index 35c21fa..d63984a 100644 --- a/packages/oc-template-react-compiler/lib/compileView.js +++ b/packages/oc-template-react-compiler/lib/compileView.js @@ -1,135 +1,134 @@ -"use strict"; +const fs = require('fs-extra'); +const vite = require('vite'); +const react = require('@vitejs/plugin-react'); +const path = require('path'); +const EnvironmentPlugin = require('vite-plugin-environment').default; +const hashBuilder = require('oc-hash-builder'); +const ocViewWrapper = require('oc-view-wrapper'); +const { callbackify } = require('util'); +const viewTemplate = require('./viewTemplate'); +const reactOCProviderTemplate = require('./reactOCProviderTemplate'); +const cssModules = require('./cssModulesPlugin'); -const async = require("async"); -const fs = require("fs-extra"); -const hashBuilder = require("oc-hash-builder"); -const MemoryFS = require("memory-fs"); -const minifyFile = require("oc-minify-file"); -const ocViewWrapper = require("oc-view-wrapper"); -const path = require("path"); -const reactComponentWrapper = require("oc-react-component-wrapper"); -const strings = require("oc-templates-messages"); +const clientName = 'clientBundle'; -const { - compiler, - configurator: { client: webpackConfigurator } -} = require("./to-abstract-base-template-utils/oc-webpack"); +const partition = (array, predicate) => { + const matches = []; + const rest = []; + for (const element of array) { + if (predicate(element)) { + matches.push(element); + } else { + rest.push(element); + } + } + return [matches, rest]; +}; -const fontFamilyUnicodeParser = require("./to-abstract-base-template-utils/font-family-unicode-parser"); -const reactOCProviderTemplate = require("./reactOCProviderTemplate"); -const viewTemplate = require("./viewTemplate"); +async function compileView(options) { + function processRelativePath(relativePath) { + let pathStr = path.join(options.componentPath, relativePath); + if (process.platform === 'win32') { + return pathStr.split('\\').join('\\\\'); + } + return pathStr; + } -module.exports = (options, callback) => { + const staticFiles = options.componentPackage.oc.files.static; + const staticFolder = Array.isArray(staticFiles) ? staticFiles[0] : staticFiles; const viewFileName = options.componentPackage.oc.files.template.src; - let viewPath = path.join(options.componentPath, viewFileName); - if (process.platform === "win32") { - viewPath = viewPath.split("\\").join("\\\\"); - } + const componentPath = options.componentPath; + const viewPath = processRelativePath(viewFileName); + const publishPath = options.publishPath; - const publishFileName = options.publishFileName || "template.js"; + const tempPath = path.join(publishPath, 'temp'); + const publishFileName = options.publishFileName || 'template.js'; const componentPackage = options.componentPackage; - const { getInfo } = require("../index"); - const externals = componentPackage.oc.files.template.externals || getInfo().externals; - const production = options.production; + const { getInfo } = require('../index'); + const externals = getInfo().externals; + const production = !!options.production; const reactOCProviderContent = reactOCProviderTemplate({ viewPath }); - const reactOCProviderName = "reactOCProvider.js"; - const reactOCProviderPath = path.join( - publishPath, - "temp", - reactOCProviderName - ); + const reactOCProviderName = 'reactOCProvider.tsx'; + const reactOCProviderPath = path.join(tempPath, reactOCProviderName); - const compile = (options, cb) => { - const config = webpackConfigurator({ - viewPath: options.viewPath, - externals: externals.reduce((externals, dep) => { - externals[dep.name] = dep.global; - return externals; - }, {}), - publishFileName, - production, - buildIncludes: componentPackage.oc.files.template.buildIncludes || [] - }); - compiler(config, (err, data) => { - if (err) { - return cb(err); - } + await fs.outputFile(reactOCProviderPath, reactOCProviderContent); - const memoryFs = new MemoryFS(data); - const bundle = memoryFs.readFileSync( - `/build/${config.output.filename}`, - "UTF8" - ); + const globals = externals.reduce((externals, dep) => { + externals[dep.name] = dep.global; + return externals; + }, {}); - const bundleHash = hashBuilder.fromString(bundle); - const bundleName = "react-component"; - const bundlePath = path.join(publishPath, `${bundleName}.js`); - const wrappedBundle = reactComponentWrapper(bundleHash, bundle); - fs.outputFileSync(bundlePath, wrappedBundle); + const result = await vite.build({ + appType: 'custom', + root: componentPath, + mode: production ? 'production' : 'development', + plugins: [react(), EnvironmentPlugin(['NODE_ENV']), cssModules()], + logLevel: 'silent', + build: { + sourcemap: !production, + lib: { entry: reactOCProviderPath, formats: ['iife'], name: clientName }, + write: false, + minify: production, + rollupOptions: { + external: Object.keys(globals), + output: { + globals + } + } + } + }); + const out = Array.isArray(result) ? result[0] : result; + const bundle = out.output.find((x) => x.facadeModuleId.endsWith(reactOCProviderName)).code; + const [cssAssets, otherAssets] = partition( + out.output.filter((x) => x.type === 'asset'), + (x) => x.fileName.endsWith('.css') + ); + const cssStyles = cssAssets.map((x) => x.source.replace(/\n/g, '') ?? '').join(' '); + const bundleHash = hashBuilder.fromString(bundle); + const wrappedBundle = `(function() { + ${bundle} - let css = null; - if (data.build["main.css"]) { - // This is an awesome hack by KimTaro that will blow your mind. - // Remove it once this get merged: https://github.com/webpack-contrib/css-loader/pull/523 - css = fontFamilyUnicodeParser( - memoryFs.readFileSync(`/build/main.css`, "UTF8") - ); + return ${clientName}; + })()`; - // We convert single quotes to double quotes in order to - // support the viewTemplate's string interpolation - css = minifyFile(".css", css).replace(/\'/g, '"'); - const cssPath = path.join(publishPath, `styles.css`); - fs.outputFileSync(cssPath, css); - } + const reactRoot = `oc-reactRoot-${componentPackage.name}`; + const templateString = viewTemplate({ + reactRoot, + css: cssStyles, + externals, + wrappedBundle, + hash: bundleHash + }); + const templateStringCompressed = production + ? templateString.replace(/\s+/g, ' ') + : templateString; + const hash = hashBuilder.fromString(templateStringCompressed); + const view = ocViewWrapper(hash, templateStringCompressed); - const reactRoot = `oc-reactRoot-${componentPackage.name}`; - const templateString = viewTemplate({ - reactRoot, - css, - externals, - bundleHash, - bundleName - }); + await fs.unlink(reactOCProviderPath); + await fs.mkdir(publishPath, { recursive: true }); + await fs.writeFile(path.join(publishPath, publishFileName), view); + if (staticFolder) { + for (const asset of otherAssets) { + // asset.fileName could have paths like assets/file.js + // so we need to create those extra directories + await fs.ensureFile(path.join(publishPath, staticFolder, asset.fileName)); + await fs.writeFile( + path.join(publishPath, staticFolder, asset.fileName), + asset.source, + 'utf-8' + ); + } + } - const templateStringCompressed = production - ? templateString.replace(/\s+/g, " ") - : templateString; - const hash = hashBuilder.fromString(templateStringCompressed); - const view = ocViewWrapper(hash, templateStringCompressed); - return cb(null, { - template: { view, hash }, - bundle: { hash: bundleHash } - }); - }); + return { + template: { + type: options.componentPackage.oc.files.template.type, + hashKey: hash, + src: publishFileName + } }; +} - async.waterfall( - [ - next => fs.outputFile(reactOCProviderPath, reactOCProviderContent, next), - next => compile({ viewPath: reactOCProviderPath }, next), - (compiled, next) => - fs.remove(reactOCProviderPath, err => next(err, compiled)), - (compiled, next) => fs.ensureDir(publishPath, err => next(err, compiled)), - (compiled, next) => - fs.writeFile( - path.join(publishPath, publishFileName), - compiled.template.view, - err => next(err, compiled) - ) - ], - (err, compiled) => { - if (err) { - return callback(strings.errors.compilationFailed(viewFileName, err)); - } - callback(null, { - template: { - type: options.componentPackage.oc.files.template.type, - hashKey: compiled.template.hash, - src: publishFileName - }, - bundle: { hashKey: compiled.bundle.hash } - }); - } - ); -}; +module.exports = callbackify(compileView); diff --git a/packages/oc-template-react-compiler/lib/cssModulesPlugin.js b/packages/oc-template-react-compiler/lib/cssModulesPlugin.js new file mode 100644 index 0000000..85f8614 --- /dev/null +++ b/packages/oc-template-react-compiler/lib/cssModulesPlugin.js @@ -0,0 +1,81 @@ +const { transformSync } = require('@babel/core'); + +const cssLangs = `\\.(css|less|sass|scss|styl|stylus|pcss|postcss)($|\\?)`; +const importRE = + /import\s+([\S]+)\s+from\s+('|")([\S]+)\.(css|less|sass|scss|styl|stylus|postcss)(\?[\S]*)?('|")/; +const jsRE = /\.(js|mjs|ts|jsx|tsx)/; +const cssLangRE = new RegExp(cssLangs); +const cssModuleRE = new RegExp(`\\.module${cssLangs}`); + +function autoCSSModulePlugin() { + return () => { + return { + visitor: { + ImportDeclaration: (path) => { + const { node } = path; + if (!node) { + return; + } + // 如果不是module css的那么就通过 转化为 module.styl来模块化css + if ( + node.specifiers && + node.specifiers.length > 0 && + cssLangRE.test(node.source.value) && + !cssModuleRE.test(node.source.value) + ) { + const cssFile = node.source.value; + const extension = cssFile.split('.').pop() ?? 'css'; + node.source.value = + cssFile + (cssFile.indexOf('?') > -1 ? '&' : '?') + `.module.${extension}`; + } + } + } + }; + }; +} + +function transform(code, { sourceMap, file }) { + const parsePlugins = ['jsx']; + if (/\.tsx?$/.test(file)) { + parsePlugins.push('typescript'); + } + + const result = transformSync(code, { + configFile: false, + parserOpts: { + sourceType: 'module', + allowAwaitOutsideFunction: true, + plugins: parsePlugins + }, + sourceMaps: true, + sourceFileName: file, + inputSourceMap: sourceMap, + plugins: [autoCSSModulePlugin()] + }); + + return { + code: result.code, + map: result.map + }; +} + +function viteTransformCSSModulesPlugin() { + const name = 'vite-plugin-transform-css-modules'; + return { + name, + transform(code, id) { + if (jsRE.test(id) && importRE.test(code)) { + const result = transform(code, { + file: id, + sourceMap: this.getCombinedSourcemap() + }); + if (result) { + return result; + } + } + return undefined; + } + }; +} + +module.exports = viteTransformCSSModulesPlugin; diff --git a/packages/oc-template-react-compiler/lib/higherOrderServerTemplate.js b/packages/oc-template-react-compiler/lib/higherOrderServerTemplate.js index ea227c0..a9a20ce 100644 --- a/packages/oc-template-react-compiler/lib/higherOrderServerTemplate.js +++ b/packages/oc-template-react-compiler/lib/higherOrderServerTemplate.js @@ -1,12 +1,10 @@ -const higherOrderServerTemplate = ({ - serverPath, - bundleHashKey, - componentName, - componentVersion -}) => ` -import { data as dataProvider } from '${serverPath}'; -export const data = (context, callback) => { - dataProvider(context, (error, model) => { +const removeExtension = (path) => path.replace(/\.(j|t)sx?$/, ''); + +const higherOrderServerTemplate = ({ serverPath, componentName, componentVersion }) => ` +import { data as dataProvider } from '${removeExtension(serverPath)}'; + +export const data = (context : any, callback : (error: any, data?: any) => void) => { + dataProvider(context, (error: any, model: any) => { if (error) { return callback(error); } @@ -20,8 +18,6 @@ export const data = (context, callback) => { const srcPath = srcPathHasProtocol ? context.staticPath : ("https:" + context.staticPath); return callback(null, Object.assign({}, { reactComponent: { - key: "${bundleHashKey}", - src: srcPath + "react-component.js", props } })); diff --git a/packages/oc-template-react-compiler/lib/oc-app.d.ts b/packages/oc-template-react-compiler/lib/oc-app.d.ts new file mode 100644 index 0000000..56ded94 --- /dev/null +++ b/packages/oc-template-react-compiler/lib/oc-app.d.ts @@ -0,0 +1,72 @@ +interface OC { + conf: { + templates: Array<{ + type: string; + externals: string[]; + }>; + }; + cmd: { + push: (cb: (oc: OC) => void) => void; + }; + events: { + on: (eventName: string, fn: (...data: any[]) => void) => void; + off: (eventName: string, fn?: (...data: any[]) => void) => void; + fire: (eventName: string, data?: any) => void; + reset: () => void; + }; + renderNestedComponent: (ocElement: HTMLElement, cb: () => void) => void; +} + +declare global { + interface Window { + oc: OC; + } + + namespace JSX { + interface IntrinsicElements { + 'oc-component': React.DetailedHTMLProps< + React.HTMLAttributes & { href: string }, + HTMLElement + >; + } + } +} + +export interface AcceptLanguage { + code: string; + script?: any; + region: string; + quality: number; +} + +export interface Env { + name: string; +} + +export interface Plugins {} + +export interface External { + global: string; + url: string; + name: string; +} + +export interface Template { + type: string; + version: string; + externals: External[]; +} + +export interface Context { + acceptLanguage: AcceptLanguage[]; + baseUrl: string; + env: E; + params: T; + plugins: Plugins; + requestHeaders: Record; + requestIp: string; + setEmptyResponse: () => void; + setHeader: (header: string, value: string) => void; + staticPath: string; + templates: Template[]; +} diff --git a/packages/oc-template-react-compiler/lib/reactOCProviderTemplate.js b/packages/oc-template-react-compiler/lib/reactOCProviderTemplate.js index d449e9f..9a5402a 100644 --- a/packages/oc-template-react-compiler/lib/reactOCProviderTemplate.js +++ b/packages/oc-template-react-compiler/lib/reactOCProviderTemplate.js @@ -1,53 +1,53 @@ +const removeExtension = (path) => path.replace(/\.(t|j)sx?$/, ''); + const reactOCProviderTemplate = ({ viewPath }) => ` - import PropTypes from 'prop-types'; import React from 'react'; - import View from '${viewPath}'; + import View from '${removeExtension(viewPath)}'; + import { DataProvider } from 'oc-template-typescript-react-compiler/utils/useData' class OCProvider extends React.Component { componentDidMount(){ - const { _staticPath, _baseUrl, _componentName, _componentVersion, ...rest } = this.props; - window.oc.events.fire('oc:componentDidMount', rest); + const { _staticPath, _baseUrl, _componentName, _componentVersion, ...rest } = (this.props as any); + (window as any).oc.events.fire('oc:componentDidMount', rest); } - getChildContext() { - const getData = (parameters, cb) => { - return window.oc.getData({ - name: this.props._componentName, - version: this.props._componentVersion, - baseUrl: this.props._baseUrl, - parameters - }, (err, data) => { - if (err) { - return cb(err); - } - const { _staticPath, _baseUrl, _componentName, _componentVersion, ...rest } = data.reactComponent.props; - cb(null, rest, data.reactComponent.props); - }); - }; - const getSetting = setting => { - const settingHash = { - name: this.props._componentName, - version: this.props._componentVersion, - baseUrl: this.props._baseUrl, - staticPath: this.props._staticPath - }; - return settingHash[setting]; + getData(providerProps: any, parameters: any, cb: (error: any, parameters?: any, props?: any) => void) { + return (window as any).oc.getData({ + name: providerProps._componentName, + version: providerProps._componentVersion, + baseUrl: providerProps._baseUrl, + parameters + }, (err: any, data: any) => { + if (err) { + return cb(err); + } + const { _staticPath, _baseUrl, _componentName, _componentVersion, ...rest } = (data.reactComponent.props as any); + cb(null, rest, data.reactComponent.props); + }); + } + + getSetting(providerProps: any, setting: string) { + const settingHash = { + name: providerProps._componentName, + version: providerProps._componentVersion, + baseUrl: providerProps._baseUrl, + staticPath: providerProps._staticPath }; - return { getData, getSetting }; + return (settingHash as any)[setting]; } render() { - const { _staticPath, _baseUrl, _componentName, _componentVersion, ...rest } = this.props; + const { _staticPath, _baseUrl, _componentName, _componentVersion, ...rest } = (this.props as any); + (rest as any).getData = (parameters: any, cb: (error: any, parameters?: any, props?: any) => void) => this.getData(this.props, parameters, cb); + (rest as any).getSetting = (setting: string) => this.getSetting(this.props, setting); return ( - + + + ); } } - OCProvider.childContextTypes = { - getData: PropTypes.func, - getSetting: PropTypes.func - }; export default OCProvider `; diff --git a/packages/oc-template-react-compiler/lib/to-abstract-base-template-utils/font-family-unicode-parser.js b/packages/oc-template-react-compiler/lib/to-abstract-base-template-utils/font-family-unicode-parser.js deleted file mode 100644 index 948ede5..0000000 --- a/packages/oc-template-react-compiler/lib/to-abstract-base-template-utils/font-family-unicode-parser.js +++ /dev/null @@ -1,25 +0,0 @@ -const RE_FONT_FAMILY = /font-family\s*:\s*([^;\n]+)[;\n]/g; -const RE_FONT_NAMES = /(?:(?:'|(?:\\"))?([(?:\\\\)\w\d _-]+)(?:'|(?:\\"))?(,\s*)?)/g; -const RE_DOUBLE_COUTE = /"/g; -const RE_SPACE = /[ ]+/g; -const RE_UNICODE = /\\([\d\w]{4})/g; - -module.exports = function(source) { - source = source.replace(RE_FONT_FAMILY, (str, fontNames) => { - const strFontNames = fontNames - .replace(RE_DOUBLE_COUTE, "'") - .replace(RE_FONT_NAMES, (match, fontName, sep) => { - if (match.includes("'")) { - const replacement = fontName - .replace(RE_SPACE, " ") - .replace(RE_UNICODE, (m, unicode) => - String.fromCharCode(parseInt(unicode, 16)) - ); - return `'${replacement}'${sep || ""}`; - } - return match; - }); - return `font-family: ${strFontNames};`; - }); - return source; -}; diff --git a/packages/oc-template-react-compiler/lib/to-abstract-base-template-utils/oc-webpack/index.js b/packages/oc-template-react-compiler/lib/to-abstract-base-template-utils/oc-webpack/index.js deleted file mode 100644 index eabd14c..0000000 --- a/packages/oc-template-react-compiler/lib/to-abstract-base-template-utils/oc-webpack/index.js +++ /dev/null @@ -1,9 +0,0 @@ -"use strict"; - -const compiler = require("./lib/compiler"); -const configurator = require("./lib/configurator"); - -module.exports = { - compiler, - configurator -}; diff --git a/packages/oc-template-react-compiler/lib/to-abstract-base-template-utils/oc-webpack/lib/compiler/index.js b/packages/oc-template-react-compiler/lib/to-abstract-base-template-utils/oc-webpack/lib/compiler/index.js deleted file mode 100644 index 449050b..0000000 --- a/packages/oc-template-react-compiler/lib/to-abstract-base-template-utils/oc-webpack/lib/compiler/index.js +++ /dev/null @@ -1,42 +0,0 @@ -"use strict"; - -const MemoryFS = require("memory-fs"); -const webpack = require("webpack"); - -const memoryFs = new MemoryFS(); - -module.exports = function compiler(config, callback) { - const logger = config.logger; - delete config.logger; - - const compiler = webpack(config); - compiler.outputFileSystem = memoryFs; - - compiler.run((error, stats) => { - let softError; - let warning; - - // handleFatalError - if (error) { - return callback(error); - } - - const info = stats.toJson(); - // handleSoftErrors - if (stats.hasErrors()) { - softError = info.errors.toString(); - return callback(softError); - } - // handleWarnings - if (stats.hasWarnings()) { - warning = info.warnings.toString(); - } - - const log = stats.toString(config.stats || "errors-only"); - - if (log) { - logger.log(log); - } - callback(null, memoryFs.data); - }); -}; diff --git a/packages/oc-template-react-compiler/lib/to-abstract-base-template-utils/oc-webpack/lib/configurator/client/index.js b/packages/oc-template-react-compiler/lib/to-abstract-base-template-utils/oc-webpack/lib/configurator/client/index.js deleted file mode 100644 index 9f22c9c..0000000 --- a/packages/oc-template-react-compiler/lib/to-abstract-base-template-utils/oc-webpack/lib/configurator/client/index.js +++ /dev/null @@ -1,120 +0,0 @@ -"use strict"; - -const MiniCssExtractPlugin = require("mini-css-extract-plugin"); -const MinifyPlugin = require("babel-minify-webpack-plugin"); -const path = require("path"); -const webpack = require("webpack"); -const _ = require("lodash"); - -const createExcludeRegex = require("../createExcludeRegex"); - -module.exports = options => { - const buildPath = options.buildPath || "/build"; - const production = options.production; - const buildIncludes = options.buildIncludes.concat( - "oc-template-react-compiler/utils" - ); - const excludeRegex = createExcludeRegex(buildIncludes); - const localIdentName = !production - ? "oc__[path][name]-[ext]__[local]__[hash:base64:8]" - : "[local]__[hash:base64:8]"; - - const cssLoader = { - test: /\.css$/, - use: [ - MiniCssExtractPlugin.loader, - { - loader: require.resolve("css-loader"), - options: { - importLoaders: 1, - modules: true, - localIdentName, - camelCase: true - } - }, - { - loader: require.resolve("postcss-loader"), - options: { - ident: "postcss", - plugins: [ - require("postcss-import"), - require("postcss-extend"), - require("postcss-icss-values"), - require("autoprefixer") - ] - } - } - ] - }; - - let plugins = [ - new MiniCssExtractPlugin({ - filename: "[name].css", - allChunks: true, - ignoreOrder: true - }), - new webpack.DefinePlugin({ - "process.env.NODE_ENV": JSON.stringify( - production ? "production" : "development" - ) - }) - ]; - if (production) { - plugins = plugins.concat(new MinifyPlugin()); - } - - const cacheDirectory = !production; - const polyfills = ["Object.assign"]; - - return { - mode: production ? "production" : "development", - optimization: { - // https://webpack.js.org/configuration/optimization/ - // Override production mode optimization for minification - // As it currently breakes the build, still rely on babel-minify-webpack-plugin instead - minimize: false - }, - entry: options.viewPath, - output: { - path: buildPath, - filename: options.publishFileName - }, - externals: _.omit(options.externals, polyfills), - module: { - rules: [ - cssLoader, - { - test: /\.jsx?$/, - exclude: excludeRegex, - use: [ - { - loader: require.resolve("babel-loader"), - options: { - cacheDirectory, - babelrc: false, - presets: [ - [ - require.resolve("babel-preset-env"), - { modules: false, loose: true } - ], - [require.resolve("babel-preset-react")] - ], - plugins: [ - [require.resolve("babel-plugin-transform-object-rest-spread")] - ] - } - } - ] - } - ] - }, - plugins, - resolve: { - alias: { - react: path.join(__dirname, "../../node_modules/react"), - "react-dom": path.join(__dirname, "../../node_modules/react-dom"), - "prop-types": path.join(__dirname, "../../node_modules/prop-types") - } - } - }; -}; diff --git a/packages/oc-template-react-compiler/lib/to-abstract-base-template-utils/oc-webpack/lib/configurator/createExcludeRegex.js b/packages/oc-template-react-compiler/lib/to-abstract-base-template-utils/oc-webpack/lib/configurator/createExcludeRegex.js deleted file mode 100644 index c8a00da..0000000 --- a/packages/oc-template-react-compiler/lib/to-abstract-base-template-utils/oc-webpack/lib/configurator/createExcludeRegex.js +++ /dev/null @@ -1,6 +0,0 @@ -"use strict"; - -const createExcludeRegex = buildIncludes => - new RegExp(`node_modules\/(?!(${buildIncludes.join("|")}))`); - -module.exports = createExcludeRegex; diff --git a/packages/oc-template-react-compiler/lib/to-abstract-base-template-utils/oc-webpack/lib/configurator/index.js b/packages/oc-template-react-compiler/lib/to-abstract-base-template-utils/oc-webpack/lib/configurator/index.js deleted file mode 100644 index bba8224..0000000 --- a/packages/oc-template-react-compiler/lib/to-abstract-base-template-utils/oc-webpack/lib/configurator/index.js +++ /dev/null @@ -1,9 +0,0 @@ -"use strict"; - -const client = require("./client"); -const server = require("./server"); - -module.exports = { - client, - server -}; diff --git a/packages/oc-template-react-compiler/lib/to-abstract-base-template-utils/oc-webpack/lib/configurator/server/index.js b/packages/oc-template-react-compiler/lib/to-abstract-base-template-utils/oc-webpack/lib/configurator/server/index.js deleted file mode 100644 index 1d928e6..0000000 --- a/packages/oc-template-react-compiler/lib/to-abstract-base-template-utils/oc-webpack/lib/configurator/server/index.js +++ /dev/null @@ -1,93 +0,0 @@ -/*jshint camelcase:false */ -"use strict"; - -const MinifyPlugin = require("babel-minify-webpack-plugin"); -const externalDependenciesHandlers = require("oc-external-dependencies-handler"); -const path = require("path"); -const webpack = require("webpack"); - -module.exports = function webpackConfigGenerator(options) { - const production = - options.production !== undefined ? options.production : "true"; - - const sourceMaps = !production; - const devtool = sourceMaps ? "#source-map" : ""; - - const jsLoaders = [ - { - loader: require.resolve("babel-loader"), - options: { - cacheDirectory: true, - retainLines: true, - sourceMaps, - sourceRoot: path.join(options.serverPath, ".."), - babelrc: false, - presets: [ - [ - require.resolve("babel-preset-env"), - { - modules: false, - targets: { - node: 6 - } - } - ], - [ - require.resolve("babel-preset-react") - ] - ], - plugins: [ - [require.resolve("babel-plugin-transform-object-rest-spread")] - ] - } - } - ]; - - const plugins = [ - new webpack.DefinePlugin({ - "process.env.NODE_ENV": JSON.stringify( - production ? "production" : "development" - ) - }) - ]; - - if (production) { - jsLoaders.unshift({ - loader: require.resolve("infinite-loop-loader") - }); - plugins.unshift(new MinifyPlugin()); - } - - return { - mode: production ? "production" : "development", - optimization: { - // https://webpack.js.org/configuration/optimization/ - // Override production mode optimization for minification - // As it currently breakes the build, still rely on babel-minify-webpack-plugin instead - minimize: false - }, - devtool, - entry: options.serverPath, - target: "node", - output: { - path: path.join(options.serverPath, "../build"), - filename: options.publishFileName, - libraryTarget: "commonjs2", - devtoolModuleFilenameTemplate: "[absolute-resource-path]", - devtoolFallbackModuleFilenameTemplate: "[absolute-resource-path]?[hash]" - }, - externals: externalDependenciesHandlers(options.dependencies), - module: { - rules: [ - { - test: /\.js$/, - exclude: /node_modules/, - use: jsLoaders - } - ] - }, - plugins, - logger: options.logger || console, - stats: options.stats - }; -}; diff --git a/packages/oc-template-react-compiler/lib/verifyConfig.js b/packages/oc-template-react-compiler/lib/verifyConfig.js new file mode 100644 index 0000000..c99218b --- /dev/null +++ b/packages/oc-template-react-compiler/lib/verifyConfig.js @@ -0,0 +1,212 @@ +const chalk = require('chalk'); +const fs = require('fs'); +const resolve = require('resolve'); +const path = require('path'); +const os = require('os'); +const immer = require('immer').produce; + +function writeJson(fileName, object) { + fs.writeFileSync(fileName, JSON.stringify(object, null, 2).replace(/\n/g, os.EOL) + os.EOL); +} + +function verifyTypeScriptSetup(componentPath) { + const paths = { + appTsConfig: path.resolve(componentPath, 'tsconfig.json'), + yarnLockFile: path.resolve(componentPath, 'yarn.lock'), + appNodeModules: path.resolve(componentPath, 'node_modules'), + appTypeDeclarations: path.resolve(componentPath, 'src', 'oc-app.d.ts') + }; + let firstTimeSetup = false; + + if (!fs.existsSync(paths.appTsConfig)) { + writeJson(paths.appTsConfig, {}); + firstTimeSetup = true; + } + + const isYarn = fs.existsSync(paths.yarnLockFile); + + // Ensure typescript is installed + let ts; + try { + ts = require(resolve.sync('typescript', { + basedir: paths.appNodeModules + })); + } catch (_) { + console.error( + chalk.bold.red( + `It looks like you're trying to use TypeScript but do not have ${chalk.bold( + 'typescript' + )} installed.` + ) + ); + console.error( + chalk.bold( + 'Please install', + chalk.cyan.bold('typescript'), + 'by running', + chalk.cyan.bold(isYarn ? 'yarn add typescript' : 'npm install typescript') + '.' + ) + ); + console.error(); + process.exit(1); + } + + const compilerOptions = { + // These are suggested values and will be set when not present in the + // tsconfig.json + // 'parsedValue' matches the output value from ts.parseJsonConfigFileContent() + target: { + parsedValue: ts.ScriptTarget.ES5, + suggested: 'es5' + }, + lib: { suggested: ['dom', 'dom.iterable', 'esnext'] }, + allowJs: { suggested: true }, + skipLibCheck: { suggested: true }, + esModuleInterop: { suggested: true }, + allowSyntheticDefaultImports: { suggested: true }, + strict: { suggested: true }, + forceConsistentCasingInFileNames: { suggested: true }, + // TODO: Enable for v4.0 (#6936) + // noFallthroughCasesInSwitch: { suggested: true }, + + // These values are required and cannot be changed by the user + // Keep this in sync with the rollup config + module: { + parsedValue: ts.ModuleKind.ESNext, + value: 'esnext', + reason: 'for import() and import/export' + }, + moduleResolution: { + parsedValue: ts.ModuleResolutionKind.NodeJs, + value: 'node', + reason: 'to match rollup resolution' + }, + resolveJsonModule: { value: true, reason: 'to match rollup loader' }, + isolatedModules: { value: true, reason: 'implementation limitation' }, + jsx: { + parsedValue: ts.JsxEmit.ReactJSX, + suggested: 'react-jsx' + }, + paths: { value: undefined, reason: 'aliased imports are not supported' } + }; + + const formatDiagnosticHost = { + getCanonicalFileName: (fileName) => fileName, + getCurrentDirectory: ts.sys.getCurrentDirectory, + getNewLine: () => os.EOL + }; + + const messages = []; + let appTsConfig; + let parsedTsConfig; + let parsedCompilerOptions; + try { + const { config: readTsConfig, error } = ts.readConfigFile(paths.appTsConfig, ts.sys.readFile); + + if (error) { + throw new Error(ts.formatDiagnostic(error, formatDiagnosticHost)); + } + + appTsConfig = readTsConfig; + + // Get TS to parse and resolve any "extends" + // Calling this function also mutates the tsconfig above, + // adding in "include" and "exclude", but the compilerOptions remain untouched + let result; + parsedTsConfig = immer(readTsConfig, (config) => { + result = ts.parseJsonConfigFileContent(config, ts.sys, path.dirname(paths.appTsConfig)); + }); + + if (result.errors && result.errors.length) { + throw new Error(ts.formatDiagnostic(result.errors[0], formatDiagnosticHost)); + } + + parsedCompilerOptions = result.options; + } catch (e) { + if (e && e.name === 'SyntaxError') { + console.error( + chalk.red.bold( + 'Could not parse', + chalk.cyan('tsconfig.json') + '.', + 'Please make sure it contains syntactically correct JSON.' + ) + ); + } + + console.log(e && e.message ? `${e.message}` : ''); + process.exit(1); + } + + if (appTsConfig.compilerOptions == null) { + appTsConfig.compilerOptions = {}; + firstTimeSetup = true; + } + + for (const option of Object.keys(compilerOptions)) { + const { parsedValue, value, suggested, reason } = compilerOptions[option]; + + const valueToCheck = parsedValue === undefined ? value : parsedValue; + const coloredOption = chalk.cyan('compilerOptions.' + option); + + if (suggested != null) { + if (parsedCompilerOptions[option] === undefined) { + appTsConfig.compilerOptions[option] = suggested; + messages.push( + `${coloredOption} to be ${chalk.bold('suggested')} value: ${chalk.cyan.bold( + suggested + )} (this can be changed)` + ); + } + } else if (parsedCompilerOptions[option] !== valueToCheck) { + appTsConfig.compilerOptions[option] = value; + messages.push( + `${coloredOption} ${chalk.bold(valueToCheck == null ? 'must not' : 'must')} be ${ + valueToCheck == null ? 'set' : chalk.cyan.bold(value) + }` + (reason != null ? ` (${reason})` : '') + ); + } + } + + // tsconfig will have the merged "include" and "exclude" by this point + if (parsedTsConfig.include == null) { + appTsConfig.include = ['src']; + messages.push(`${chalk.cyan('include')} should be ${chalk.cyan.bold('src')}`); + } + + if (parsedTsConfig.exclude == null) { + appTsConfig.exclude = ['src/_package']; + messages.push(`${chalk.cyan('exclude')} should have ${chalk.cyan.bold('src/_package')}`); + } + + if (messages.length > 0) { + if (firstTimeSetup) { + console.log( + chalk.bold('Your', chalk.cyan('tsconfig.json'), 'has been populated with default values.') + ); + console.log(); + } else { + console.warn( + chalk.bold( + 'The following changes are being made to your', + chalk.cyan('tsconfig.json'), + 'file:' + ) + ); + messages.forEach((message) => { + console.warn(' - ' + message); + }); + console.warn(); + } + writeJson(paths.appTsConfig, appTsConfig); + } + + // Reference `oc-template-typescript-react-compiler` types + if (!fs.existsSync(paths.appTypeDeclarations)) { + fs.writeFileSync( + paths.appTypeDeclarations, + `/// ${os.EOL}` + ); + } +} + +module.exports = verifyTypeScriptSetup; diff --git a/packages/oc-template-react-compiler/lib/viewTemplate.js b/packages/oc-template-react-compiler/lib/viewTemplate.js index 837bca5..5a64bf2 100644 --- a/packages/oc-template-react-compiler/lib/viewTemplate.js +++ b/packages/oc-template-react-compiler/lib/viewTemplate.js @@ -1,32 +1,31 @@ -const viewTemplate = ({ - reactRoot, - css, - externals, - bundleHash, - bundleName -}) => `function(model){ +const viewTemplate = ({ reactRoot, css, externals, wrappedBundle, hash }) => `function(model){ var modelHTML = model.__html ? model.__html : ''; var staticPath = model.reactComponent.props._staticPath; var props = JSON.stringify(model.reactComponent.props); - var randomId = Math.random() * 10000000; - var reactRootId = "${reactRoot}-" + randomId; - return '${css ? "" : ""}' + - '
' + modelHTML + '
' + + window.oc = window.oc || {}; + window.oc.__typescriptReactTemplate = window.oc.__typescriptReactTemplate || { count: 0 }; + oc.reactComponents = oc.reactComponents || {}; + oc.reactComponents['${hash}'] = oc.reactComponents['${hash}'] || (${wrappedBundle}); + var count = window.oc.__typescriptReactTemplate.count; + var templateId = "${reactRoot}-" + count; + window.oc.__typescriptReactTemplate.count++; + return '
' + modelHTML + '
' + + '${css ? '' : ''}' + '' diff --git a/packages/oc-template-react-compiler/package.json b/packages/oc-template-react-compiler/package.json index 6aa3081..66445a1 100644 --- a/packages/oc-template-react-compiler/package.json +++ b/packages/oc-template-react-compiler/package.json @@ -22,38 +22,27 @@ "email": "nick@balestra.ch" }, "license": "MIT", + "types": "./lib/oc-app.d.ts", "dependencies": { - "async": "2.6.3", - "autoprefixer": "9.6.2", - "babel-loader": "7.1.5", - "babel-minify-webpack-plugin": "0.3.1", - "babel-plugin-transform-object-rest-spread": "6.26.0", - "babel-preset-env": "1.7.0", - "babel-preset-react": "6.24.1", - "css-loader": "1.0.1", - "fs-extra": "8.1.0", - "infinite-loop-loader": "1.0.6", - "lodash": "4.17.19", - "memory-fs": "0.4.1", - "mini-css-extract-plugin": "0.8.0", - "node-dir": "0.1.17", + "@vitejs/plugin-react": "^3.1.0", + "chalk": "^3.0.0", + "fs-extra": "10.0.0", + "immer": "^9.0.6", "oc-external-dependencies-handler": "1.0.8", "oc-generic-template-compiler": "2.0.5", - "oc-get-unix-utc-timestamp": "1.0.2", "oc-hash-builder": "1.0.2", - "oc-minify-file": "1.0.15", - "oc-react-component-wrapper": "1.0.2", "oc-statics-compiler": "2.0.11", "oc-template-react": "2.1.0", - "oc-templates-messages": "1.0.2", "oc-view-wrapper": "1.0.3", - "postcss-extend": "1.0.5", - "postcss-icss-values": "2.0.2", - "postcss-import": "12.0.1", - "postcss-loader": "3.0.0", - "prop-types": "15.7.2", - "react": "16.9.0", - "webpack": "4.41.0" + "react": "18.2.0", + "react-dom": "18.2.0", + "resolve": "^1.20.0", + "vite": "^4.2.1", + "vite-plugin-environment": "^1.1.3" + }, + "devDependencies": { + "lodash": "4.17.19", + "node-dir": "0.1.17" }, "files": [ "index.js", @@ -63,4 +52,4 @@ "README.md", "LICENSE" ] -} +} \ No newline at end of file diff --git a/packages/oc-template-react-compiler/scaffold/src/.eslintrc.json b/packages/oc-template-react-compiler/scaffold/src/.eslintrc.json new file mode 100644 index 0000000..fcf5ef3 --- /dev/null +++ b/packages/oc-template-react-compiler/scaffold/src/.eslintrc.json @@ -0,0 +1,3 @@ +{ + "extends": ["react-app"] +} diff --git a/packages/oc-template-react-compiler/scaffold/src/app.js b/packages/oc-template-react-compiler/scaffold/src/app.js deleted file mode 100644 index 34bbfe3..0000000 --- a/packages/oc-template-react-compiler/scaffold/src/app.js +++ /dev/null @@ -1,11 +0,0 @@ -import React from "react"; -import styles from "./styles.css"; - -const App = props => -
-

- Hello {props.name} -

-
; - -export default App; diff --git a/packages/oc-template-react-compiler/scaffold/src/package.json b/packages/oc-template-react-compiler/scaffold/src/package.json index 025e976..fb538f3 100644 --- a/packages/oc-template-react-compiler/scaffold/src/package.json +++ b/packages/oc-template-react-compiler/scaffold/src/package.json @@ -2,25 +2,43 @@ "name": "base-component-react", "description": "", "version": "1.0.0", + "scripts": { + "lint": "eslint --max-warnings 0 --ext .js,.jsx,.ts,.tsx src", + "build": "tsc --noEmit && oc package .", + "test": "vitest" + }, "oc": { "files": { - "data": "server.js", + "data": "src/server.ts", "template": { - "src": "app.js", - "type": "oc-template-react" - } + "src": "src/App.tsx", + "type": "oc-template-typescript-react" + }, + "static": [ + "public" + ] }, "parameters": { - "name": { - "default": "World", - "description": "Your name", - "example": "Jane Doe", - "mandatory": false, - "type": "string" + "userId": { + "default": 0, + "description": "The user id from the user database", + "example": 0, + "mandatory": true, + "type": "number" } } }, + "dependencies": {}, "devDependencies": { - "oc-template-react-compiler": "*" + "@testing-library/react": "^14.0.0", + "@testing-library/user-event": "^14.4.3", + "@types/react": "^18.0.28", + "jsdom": "^21.1.1", + "oc-template-typescript-react": "4.0.0", + "oc-template-typescript-react-compiler": "4.1.1", + "react": "18.2.0", + "react-dom": "18.2.0", + "typescript": "5.0.2", + "vitest": "^0.29.7" } } diff --git a/packages/oc-template-react-compiler/scaffold/src/public/logo.png b/packages/oc-template-react-compiler/scaffold/src/public/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..5164af831424043a63c56ad82e249ec4395eb0fb GIT binary patch literal 57405 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≥knV01t4CE?J{~ z*4hklc^w)UgJ0k=_=rN+33;urdEf{>K&Vd8hyR%TM}%^ce9yQplt(zPH#2!zSx`%o zG$8|W+`YA@ZSXncY<%hC<=CypOYTqTw))=mv^s7m^p>;rZ4!gdk8nsmezw)yXnXWk zL$%M@O?U6NO?%&fYuL7Hx)OlgWL#gE6cUd~u3KBOE?_3dAy&{isGv+1>NKP$FP)vn z`MXooNtjYKB%|yg!OMq%4PUn@cm9F(94>yTvY}lz>+UyL@5t8;@9)de`!GU2=hF0g zi5%KG!)e$&Ov4)QAKJ1K0cauhOG>G+g@Wp(qInWP)If7O4-KkhMI>Z3;t{>`jNlV) zEU};wADnZ?Cyyk=IYuCg01q&{$r9`ts&`1F>nHex0P8zql;6{GoIA8!UYh=7E zs*rxuy+RyvM<;Hg@7=ZBCg1;V8`yk1pv|cVcS>D4@dI}M{@>0km*DfcWnOtjiwti( zJm=}nH!yNHj8Dn;U^h%D1)`*K4)ShF?IjclgpHT?0VVDIB1r3sgZjIGtN&9`*EY4C zpt$~Qq(4HzYs;&fOY#mtIV`B)j5klrGrQ)eFm1-&xV#nOM5X=}+wjoniqSo-&Kn51 zMg24^H2VcUQEHxOIlJxg9Ur%Uxc^!ku3^+O`qY&G}@{LtUqgyo+=;Nx0O>vVSCck{|= zxZC^;OsPu#lGnA))Ue~yY8r;!u=7EgD^|3b&K2M%!~1|*wm&+nbZrB~*2)reoxmY* zbO&u=3&bIf>;V}CBa{hmOngDw00tBTlX`6EAYLNBk2_sen2nT2SMJ?@z$Wu)^;O$P z$Kb{1o4s^F%eNAaj>-7O;q2b-LKXcdZw(a1r9F4YJ2|#W%VJPt{#=t}O@{|V; zAS)0-`6BR?@-k9ZCfYjYGD*=ZZf7jDCWjZc4-_`&Qo{5I=8OiQTaC(I&PfPtYHELU?9#)zow*nzt|Nh{Y5X z$OaL6M0tSlkG+)lxALGOUy)WgpNy2`8tS1}-yij;U2*MvGbFJe0f z=8}BL@E;E8n{aa)l=*u?k%YBN16X?-7{pgz_t)$o{@N?rj#G1;o>u=3w$<^1uu*NR z^W8i@b1{CwaNG4MoOa>^4ZA+Q!OmO0YziFiB}d!TE(9Q%NZG=mOR^YREUZTconUg1 zWXa(qIeud+>gdT96oBy`6J1SbiQ@sEN7?W#oBX}^Sl{lIsvGSg%SP;+fm`kHMXxD7 zAv0RygokguX*aAf0QoeGH^YU)m0HXuR5+MBqBHhj=)I*-q5cCA{+G$+$R%dpPcD;S ziqQ->^*^~6^z6+Ph=>U62t+O)kWP`H;6MhV4?ZkQehw!jG-N(%nfD~!04}K;Rn2GM z4dVM|d@SbRUOX3uQIWT<8FjRfF>R}#f%|y4&a(hZA07K?v>J>*JU()?;eU49>~B11 z=a1<19PPIzxzpF?S+OnzAR10#Y%IxOc@ffu2*Y)QW)N{2+FUG}x=)3ZiKEk_!m)95 zB$R1fOu)y_Bul-!S1IreZ1{$1j^=Rgz%AB$&5wXI_>YwfreSjt1o3N(&9*!p8Nw?% z*5h+G=aDbl82nv-BC=^^yN$A%3<}}sh(if6Mn39W;t>_R3P}ay{JO9&uVmE9}!!&FwZ%R}ElE)|RXn}VR;(_-^G@obIv6bbJCBh)WS$zRy;h-GB zJ^F_T&Wo8OR&2#Q0YEKzl;v>Iy8hVfzPbAd5^5k?W3(uGqHvI z!;5im#H_Yxtn5n9g>lMgB{E$;oje~|4@5u$Ae<$+kvm~{OmYPG3^A1_<@69bVa2Cx z(t{tc9xOI@OuXf3$!qbsl3&G7AcWX7zdBOS7C+y2O6Yhb@G1!M}e@+BM^=pbHc@Qg^i3f(DFM@uZ5cW0M ziq7?L3YQ;IhkCV2R3X<@4RFjR`4HrMQe%Op>#sIAjAB#_p3ds1j(oBFlrynVeE{o2 zSg4L2w>oBNTYb{)Yb-mAyB<5G*;xJlMNGG57q@M6B6%{_v(I*ZZHvwP@}o9&G|#ed z*YAbr`Cp4q0yXwS7XlD}HT`!pHyTJOJBorvGD^9O>@4Wg8IDz&nfb53Xd*m`DI_6k zZobPV-}g@I+qJB*J=Nu>yu|GJzlpuubBp>jFTm>#79kKVFJQ4Yc4?%B5%f#+t7Qf_T*er35f|4PfAkEj3V;Rcg(;V=w45kiV!Quj+s3xIbq$PmM^03w9{bMi!=95p+<&n7@S0O`~_dCylTZR?a1XltAh)xat zx)4B_39f5W)}FXXg!6y7ULYq7op~k-brk=yPK2P2$)|Hg*`eJz_T9PO2L5XqY-yx3 zLw$1#7juli?422pFN~r6BewgNO*Zq^$Bmz4?wI&VpqDSi-Jn=}uP}#>^a-IW0f_Q_ zz)!9;EZHIpv?M>}S~@n1;S4fHl!Zw+C{&01;PTI6o#$_?=ioN+X?Lhinqea^`Ah3r zbTu|4#H}4VUh=aL(=hz1aXqM%1~CmgmH1`U`T zMEFAX91)QEw%%)#?|p~$?Rd-q?T+yDBhP=U^(^^O>+QwxVl3$*jC8*9!8!_#fb>0= zUaVjqW#U7TRnxF7oQBP3Bhmi)uBnscb>3U*K2h)h7%P!tl#ush>O0jjXe32uyG7^qwb5egq>BXQ*S#>qiS@aU&@|DdIgRVF?a5(YB zJ(x%T%R^@8t;JURQ}8()taY3s>CoO0JMg7-HtieBb=5mIi|?lXVQkoN@x9BbD*?z2 zl$v=ckp&Vcqa@f3OExW|W3T}%HJ|4ioPOfNAF_#$e+0i!rMviAG`M!bwN`u4A6d^C z_{71nBu>F*E->4^s*VeH!;U2Yar^h|g&4`<%&qxInue_(v6+~LdH2W_bEkC}gA;lM zRqQVJXX2F-fC@SU2`^!#g*2Abuk2|RT#gEfu{bJECcGk~34G_zz#-0w+zZ5SNWyat zxyInFzyq*!ks{(&@Ragwr^ADm2%&$2q!U|Qv)k<5cft9v%kyIwKB?baSi{I~=UuyO z>gOJ^=?4yX?#H9pR`1NT9-}J(h{of_!Xr$GMqQUu87H^1z;~k?T=jXI^u52fo&!(R zQB@YOz&!HOKeL`W7fDtn&RBumrvJ`A)KTe)Of;FNiQ^L#Qk9=;q&DcE3ZS`j&;J$M4abJ8Nn|HP7a2ZyrqY2Y+Xh=$BW2Xtq zIK8TW+XFV~9^B8f{Xqw`J5IsZ8DIEz%NAcdx&`KTyc+8&0#MJ;9_xSNE;~HuIgQwr zWqv_YTfDf}q@2#VxH$lATZJBa9(o8iENmRuO_9o7<|6|S4XS7VMM73LbBf*>MDX!z z;?B+BAU-%pK*@PQm~)Ik)EE%Jx5PgohXxJjblue$>Ws2LNj{0GLxP8nY*NvF*F$nh zPWls(`muJ{=_W)zPVkl0__5lEdtNXlxdL}WQ~B|nw&9kN&)|N!2e*#yu48_A_0<^Z z9p$$ASi7Pt0Vr&wFr?B&$uE!_BpoEfNM{o6=NVk{-<|axm~6Fcehn{md@WvW3L`ll z=`2M08Rz30FEFlB;%->y0F>Dvo;%`$ka<%}JZ6J6M??HZc_dE5wsSWu-4{tGbJ}oJ zU?$k<_W?zKjNUs==PB4${wdrqMrYlyx4jTNvSA;6#PGUN(BK#5JUH!mOI$#|1^V%? z#dBD7Dr;N)0&J_#(+^Hv-Ek~*B>?4tN0EpIqu;pn^Mzo9|NLF!JvI{_{V?w7`4{9n zZ2944nHqwQJ@?n~9W&hee!LS$LAU>7os@wMx1s!Y{GkCAsUHuWPQY}lr%hbs6CiUh z!YHU=kjH1kW}fTL75x`CBSzVO{!?E@L`r)2NRl1Sz~_O>`#t(!7em;RqJUPqE`^X1 zyPWeQxJL!SUqM`MckUfh3Ix^6Q(maeHSkdpK11!2t<4$emtagc+T;kVQ||!_!xEN?FI}w zUD{S3YxBDjfM{s6O`4M%8*_k^EOs)cT7xS-J*ov}TYSBZ+<+Si`Lf>=l_~;I&%qtm zx8)uio^?f?7F_3H;Nb%EXcAwR-V}gVn_YwveGS*2eHZZ{&gAQk8Fj>BN`1?0j_g5* zRplV$*a|*j68n*fLHR0+39E_U6_i?#W`3u}`_}4K}a|7pPO3`!uV)@DD7T^HhA# z{sgC4nD@>&57V$Ub%Hq!ivXl`9!$0PT+vvPrePT2^R=y!h7V=NmpV;92apPO$w!4A z>pJ2sJ!laLgM0zo`T;f#vVfig%`297Qz*%B~e=HysK`C*d+Jvk2Bpx`e;(MLnicv5h%jlF24x4=p>qCEvX9`zi3;%eh z&HK&%@=mi4Kh$}xhe4Ikv;E$)#zpX9*K}b7lY0aU#HJpas@lSO;d$C|Xb@*ZHnfkOk_*e_)z=qeuM#_mdC5~Lf?}o7t zJwPOXl#|M*(W7+MI!9C>Kquo&c-g_VD@GXpR0{<1YziLEJgMVBfjA*a^a;oF3AIj< z1j9-CkE8bi6`FM^$K4@*@slHs|HL$hyG!j7*I#bF8(&Gpy3$W`eQ2;9_SowVA<%tt zso8fojgFS@F5;+XT9@@0o%EJiT?s&8%Wm|P#T=eI#!ZF2d)F#ob*R1kFD$#@c~w;> z2X;1wDbq0x+gU#i>s$4|HZt$n0#Ma7?C?QM!#3f$F;2r6XwYr!%QzG2fr&ZqRSzWI zzeH3n1%*6_m}&RSj@V4@@av$G?yt zfsz`q11Kgn$$2{Y_&D-z;HRI3wISH`msgu{#M3T4w!`d?zU}u9b>nGzPWA4uM?jJr<1?^YJY0JlXkq( zk?TW$j3B~CJR;t9qT6@a?4Pi$&UY*CJk%bW+v?Yz>pGp_dwSX{R$bf0DOi|O&3d;z zXalRhP%W_xJ+Q-S8y~TZ?=T%NY4%ewPsRn@wXRO1p5>T^orNjdF{C*d33DV=-VHl^ zfV*KM`p{mPH66A7hmzLH5k)w-yjvs@aLRW97!lReVIRz8>cN%Ai zUdXW5G`b+HOytZ6@svE?si;Q-J+R1~DT0FvjRz^uHaM<4CbGaSWu1}$Iu)3f>qxJv zlCYemQ;y%`;^}6;a}{n%#HffFOdj(A^Br{Kc?_=`_B zdjo>tlPLJGxm^iBVL|Mt{uQ6KK8%1yn=-7gjJs(Vcf%S|wjGABb9DoB>_t8s)|j@g z!jvBNN4twZI-QS89F{vN5t2u0l#r~GxC}`8L!NHzqa8oXt%ErgU_LG!__5>?U8>VN z3i%X)MTpG@@g`exa^4|7WO>=uS3Zv{dFwEUrjyM6!#feX7&e>J{NfjR2r=AmM?^ncw+s;N&7l&@-SNZA(b zVC>n@03Ca=)oxgLTZ{te_z{HYt4ai_0wrZVz{CiMfg(mTbYA5JBX#0ZW{kXto}j{+ zLP?B6=n{MgtxTZtf2a+m!9#Q;pbiP{;tO49KS5HW08t8(q<^xfxl_#Eg1f1I1#30a zCyc!{t$Y2`7c3D*=c`HW3e-w)`^b3x5}lqru_t!W)_!x_`UXHZN_4pG?Rc z{=#4`-;jEi-`W5fd$H9tZ0APjRUaP|2|OOKnw}co$}In(Oea<#0Xom__{fnNN`59O zuR}^H?+{U=!f9AVQqd_d#$?qdQ5I6jOuVSC>xVkzB^Iczqd-pL#GoRc3MIIgpY+E| z@dNxv&&Kw$Y)8Eu=V(& zB45g74}GRo8YkJRQBW7AVSI|7cSbiR?uN;CSwRPt6NZ}-mK^R*M0ABVc3=}TI;{yW`%L-OefpU;073xnBMR4{&2)Tke z10Ed?`6wt)V2&>^iynw@4*5hoB@;(LSInty8oxLHCin_J1APjoV-7$I4bOY+SySCb zSN}5ZhiKRV;8*$ZzMrPFf1A~|V5@sPCA=QQ&X`-L5nZ!q#qpVj@utLGo8^;|=?)u? zp8`i7DuBX>K#!%m0acFiC07v1_4QA@F0qpqnccURGF$n$j8|O z52P0i#K8lJl$9P)FMTB<$yb9yD%(~i@Dzu=7WWcO!%+DYPsbF1CiHBz#o0epcM-qC ze&x?r*IoF+O8B_k4XYXjb!!^7sJR>B*)aLDdu(#(a7aan6ixDz7^SgJ&eY2h5E4C| zSwRcTaZVxt^ihw9;m4*TXg`7jgx-=97nT0 zbPDvwF$JJ!OnNWg=W(FA8?JwwKX*`7dp;K7@UG{^ls)#PWVDSnC$U^{S9G=>x6vQm zb<9ffO^J<(Hzm?5!}-15os$l!#6XbzHG+bVS_-&3Hj$81;D|t!6AB)qB!&X7!IAP= z23?9FVxfSN$de|;DIu1tT{q`LIT4UteM0b0y0N{{y2OEMyY_{Q)45Zw2aY-b&Fp)? z&Kvkbb%!v(EV{0`j!@nVv*_C9daSL~wytc3pG?SHY@1H4pQmTFBbKea?U=RVyP_Lk zgEX|?r(yI<_sIwhC2`@bI7d)Wu<*zsM0Z|A5;?M%5g3U)#0TdT2$%ymXUfZEl^96BgiiicRMjOM>KvmZDqapaInQR1ry-|64;)nh zS}^!Io7wwdb@TZp3w~|1Q(Ay6$o}RVva(12CmCZW1}Ct-Y8`0p?f7>1zGGIRayM)x z+0vyWUZgw$;kYS~#0aR)i9J%KfCAbSw1^{m^2lLgDru~D=l7|XqcJ!TCv@?A(~(FC zkvyoE2Q<>Byy{u&)3H3@<;;>GY7sIcljEGuoJu_~<^a@}?X#y(e1CNx@OiImUSD0; z2xahL5Nb^Owph0QzDDTDEo&M!yG{q*beXOCdL8U&3jA^^HwMeer(tkU_@T!eI<2QX zr|3Xeb_mZLEwJRHiNFUWh~U(bRjmR8atS4P)zP~GNe|-Iad06#5?+!oH1Q3tj#?%! z2fS1(T?miVLRwUxK0nEOV9WvNl8GO*2|e4Yo6YCF2B%inRY5PR-VL+Izg&fV@?i9u zicwHEo(=2AG;DUofN?9{l*j>t1`TStJ0*{KfS`Bck7YS6@$ru?&wmB3kQ5+9OQsPC zcosc{fI3yh)JOOQxp0Zult{?`JVUlCCXOzbdO?BFhm z17iw6Q+rq2g%ke0x*y_suj<-rC>Nr+Al;ZAzh(ScUe#{c>Tlyq1P2?nA9J~CH;gwW zx(mvG!81xl&Vvc-J>U=}cU(F=H6bjWc>;n?`5}3@L%}B;Kj=x~9NGs1((!-JA3>!g zs+4un1TgL>7&(YXZwtW@ln5*8Pv=gp9vD*qS~BUKc!AL2>K@3S_o}Y0gbqwGJOAqD zs=ZI(^SI+}8upB)n<}&61C}ue9b=kXH4WRx-7t4T`eN>bDtL*`8lwlEV|2khgN8e? zpA|$%8;-4W;wnCDgt*{Hd8AWDe2TzAyow~=5jdPM}B7cEvPo%SyjI zbWer`rQB)Vi4_=7GP@H1EUH+wNL!m!0f)eez&qSs-E|%L~H?Ed|mU+ zFhqSE@7~D`v<2;n23JkPxDY)ue9W3uO~baXcD5c0`RU)abQe+3Xm1dkaCE_B`)0TJ$jCj}7)2;GUmPxOf+ z@(Kny6{r~$~4%4y&a_(VJNsnpC&%;Rqe~)V3hLq)b*>kJa%jm-Q&8ogm#O?Up=jGVmegy`4Y6=d-stYcvLM0uZBs_pV0+BpB z4-`ZoiZC6QWnL5iuX-I)j41%69PrE4PVYQ*q`cGyhCBYFehE~_50+K(qwe(il<0xB z0jTz#%kfZ`y{@vQeBP_-wHlSk9e|T(na#y6Q)Ak**=jpCH9}8r`J5$qtt|8{rR?BN z+&uZ+66ENzd^W6U8pho)O~c$-gR=)4cX#IH%&6pk_=e?Q;+W=ZT8)lDuu?^8lsG=8EOvAD+d^e2i`Lh-_w`m(jL3B$`C3mDQ z^jQ24hmPRGvm7}niAo0Y@Wd-33WI={Gk6fi%TXP&N!l`r(Dm@Z1L*|=@pp15D?OrK zK~oAO`D#!|CC`>pPggw2d!S_ivUe}}d066AmF*kt^Inz2Er2cF5!Ca*CtDDlL~zA- z!@BX3GPbB{H*CvFZ>#&EA_N@RVdjDUjE$R{s{;K%~C z6g_i^LlTA4shzgAc%Rm(-Rit`l>x|x4*n4=u>R@GAQ|iPUO_jSbE|3CqhA^g^@+n_ zgs9N9Ytyg=%}wEAbe_UlahBuKq2b6HPb7~ec9ca)ni7i`0V&!r9EhG0oPp?*9M31| zEh0iLKcR^e=@Y(mN%Ea7A*?4t5r}ev&=u8@EwMgzvin;+S#avKux(440#NO}SMrXO z->jrMn&-VL8MOemxGTE%d;i{o;3R@8?us6M=vF&?=-73t+znfm9FY#2Mh!e`1VJAb z*n<$D;rFG$LVCEedw;=0>Vg>XpbPe_5`W<|kR(`~s6rmZN(}1gDIc1tx`d-plme03 z5lqWGOR&?~Q>O=-0+1a({9YJi#S@}v^wB);HI}}bxxnnKiZ^ua-e^aBD`+g-jxX-H z&orBKW}S?|!|UzN4?JS4?g5w{d6cQ@ro@ALa8u$|Fbu~{fh0yibxy1yLj)8LE~L|l zgY^6(y}YKxKlUKQx5BbFwY2F z90kSsAckUz)Q8FAv_YTO&+B~iU(N3L1U@6SW9&Mfb7?aTcf;tKd4Ney&a#{qmG0E; z_605w8I<7tqD^^8U_=l-OQ3c%1+$#KAa_0@Y&ejR|9MszA`$=NK)Lv*DH>(rFM?gv zIX9Qjd`#WM&MQwJPr)9@0}!A0f@LhOY~$6x)btEgC0;jxv#^d zVB*dyhek#2xO8}85thy(Di1*jNFFX6U^dYMC4xoUU_ie7pYvznkwhIzSqBopSfya( zARbAts*x>6}p}?aBH2G_=tur#hXmN z@&U6|_o9l9*C7o~#!ZO}nvu4w0xWqXomC?YfOuv=(jW44V+E*mmRr-Q!?ATvVL^N- zOI&bd>>#}|;!^|`;#DN^CVLV&VaN|gUUv1B&m&6_o(Ly3#5)(B1*njD3V`-c!riCe z$H_-PBO||$t39Q%AG-CtS0(cnz=|zyV)oqouPq2pBDmre9Wf1S)auY)v-|!F>qGxI zy7i&z7uMH1s{#WY)JF~o8*=J|6DI~3dNIqaPk+hP<>3lYtkyqjUU)sxBY21hs~Z!M zkm}?I4(cRJC_E%7d1UeFSwSC!r~Y}QxA)X;J2|VRxyVlu}Jx8&iay*sgt3)QvsFN_g_c5D3Wo;d-T-b%j+qgb-CvNiI zx3gSRpP$S7GTpE9ii5i`4dYBS1szoY$43qT>RS6q#)As-3OGv)SuX=bc%jG)f%~b$n4*+ z{&3UX?E{e7omu@tMs&u`^=AL_YP02E3&@E(^9lKiR9pJh3g}4$@ymi0)3B?T{I8vV zIld#`+uDUTJYseWrezOf#x{)W)tER9tK1FaD>`VnV6DJI2Ztjo-~jG{N7W@5xhHF9 zM3Li@M}&=_!kKkaJAz2!naIhhDJA(r$$1yLP_&PZBr?xSzVr`{p$E$hT-{CkcXyZH zF>`Be9>>i3q%b%1;t0T z$V)5=t|QJ#mh($`Bso&QSQ9t||v#-C`?C}1M=*Nt3?&VE8OyrsETxCc0wwbpav*4p( zF#k%kiS^U4zU>cM@4+oLh?TXAF_?Si4~^K=vyy{Wh~WCry`M9?4eLYO))WYQHmuR< z4`><&USyYo1D&}pxTp%1ba=Ak0p1*eNI@ck6YG&8AUFX@N$;eNfn)0?kf5&G=_TD+ zdPEZ|1t(&YCh^SWhp;O7Q8(H%c=N_k26%1+a6{m9>T(aP#<23e=;VKW2!oV^qhOvr zijUzTm|$I(hwN?leGG>1YxrybV9F6x-}zy)54{rehA)ux_$BTz@qN)1skZFv70{Ci z(lqRbItA!<1MA}E$(o&kC+jZ9Z1$-y7_os#t-W|BMy=oYu-RRoGTVP|3@rF>|7m(Qr6T)3yh8Vp#c+hdubL+SL|S zm6>hF`HeQio}Rt;T0Sq}0f-p{p})1hzNh2re~IO`Y3~*cv)+zHrk^w0jD?Kjm0IkE z_1^R0<0bLv`BvNATe}=Mg-CES&nMtycTj=CK0+G9(W+VU?2`o zE@h=h)H7H*YLc%8h19YhEv460?1h=%{{LaR^%29Tjq0jS7rM~{LnCJYz0#*!ccX)8 zgY8n54O>so`z@PrVfMZo-k;rg{cxc7MTY0>P2YVCrz#tS@sj3WGC5(nXYXf*SR!`3#!7$n{`0z@95A%f&{V*0W@Uzp+D^E|o;Mykr2&k?2 zW+nWj0XYrh?E|Gmm!7a1H~ui?jvV6>#MUX9U2;Rsmb_xbrnFff;>@=yZNV!#A}A&=;V-l)LI>a=Q5p66adXr>kV!z}At5I2s&(Bss&P3HkR)1? z>-Be*i38OJfbHm%XmTs=!aC1iW5f1#(4btMYBu%k(~CgWebWKDa+hzZe+*Nu(FC8& z9>NyzOSAW0|BCE=)xn1nD+3TKvfupgL)lyJ{Zktpd`PQaN*`CPMc$P1(ww=x^~3+ zCzk7**wq2Ht^x-#0DC&Q#uJI$ofieKCZh0i1}yVWig_Sl$;*x!BNg%T%BU7*`H;&m z0Y#taq#GF_NvyO*r3ytL$^|6cTz@BRI&NNOcI)5cH}JpBh7L4U=YWaRN9=-U580ec zT14|b7572p)ZfRM?HE>m80!J=yT@!z{Y<2?SF&s?-l+EmyXN}KvYTJ<!n@8DCp^-CNA2!n=+oEw~1&xf7eL zE8Du%4)57x{ZpqN3-@EA-M$*Tg6}h1c56#M!_)XsSMovyqVQz7wPKTqaW@2CNU~%QWTh+UU2t*My%xgtYaWx(D5L~ zsz~G+`j_ERA7{#Q`N{-I3XG0b&%cmg1Wtz)5);o5QabO)5AwG^X7&VT1MLz6`s|Ai z+qAhH`G9k)@%)kKv3A_PkLtLM{*~3(mR*aR)LITHiw?I$N*f(gl z@cCE_X%43Q?zzLJzu-k3S#%qrwtu^2-~OQ4{r>?+qhVL4k^ZT+|6&B=u$TTh>t=Nza*G+5g3T* zwZJ7D1@TSdNzyX0QV8V6n_mmQ1WQJ$Cy$Ujl4LsT5Fo)4C7qL20;IzZ{w%ghyaDvA zMZ(uV4E6}w0`5Pp7z*=R?FS@t<}@7pll!sugcY19FTY_lK#B)#pg zYk%{sf3t%_e*r7{Wt3Y^M`?}ei49X{)F+jY(RVEjGq#L51XSF<~nd{hDVl#@7&emZe#W|zLGW;-|5 zY}xHtaNb4+t&`k7$tVX6Ieze_{{e?KrlcT&9AJ*(4C?3^xQItO61u`pUp^9!Kk^BD zR%gEIonz*F0AX;UL}00;GZ@s#SN&KHHM~sW2m<(F>zm-*r8imR4Ic}??<3r0*g*L# zV#vBZqib|#_hX&-&Dl-YeRXsM%~)d!KtZ1U`kmX5|39_&T>1C-E;^R}YR`?j$}=D7 z`0Sg_7Cgu7hTk!pxu6oQC2$c&Kz$fNH9W5{u=63?w|SpUo;|rGX*VirSk=rP`n+Y| z#NBN8WOTc9X!bR>_p-Oy$kc^$FnMk6U&z243uZV6ZW&hSMCW&O1_Nm-K*2@`DQu8^o`}c)JV(7i zI3&3-MRb;le_1C=Bsn=Gc$6L5{y2H&h|RrZ$RMGBx zzGL?Pehjw-|Ag5!ugBWXC<90TBvbHO_O&LqhaSBX0qFWBc-H{XIlJv9JiflPEz9Al z3vAyr-eyB{7>nsh?s&}00ciQjn>;*tFo2&a7Bn&!xj3O7W6U{NN}9E>W*Z)^*^Uhv z7OTz9y9_rqPD~8wSj_T3Q6X0_lyO#~z@&%D4LY&fseN?fY&}?%$wXT|s3GC(&clP8 zBZc^!gaDatA%}HZbAq5ePzuJH)@fi80?)<6Sm)t{4vFoTvg(aQm^5foq&44DzUCk% zG+$nYb#6%dYdQFTG@`Zv+LnBqu&{4c%)yIh{t$h;iwb-7U| zVo>GuZTcpJ_Vn?_POS4>07v4~Ey)6BZ?9eh;eC_q&&sOAh1NSZTJ5Si2>9%mQ#i2? zzOw;06R*UGerR+J&9eWnzMkLC-u(g)buV=#08yRn*KXU3)9=*Yx8$Gfu)PnK^aE|x z+=p4*fBz8{azBY_+3(^f{?fMSw&*&H`k*fF&MT#Xo%h?`%?E7CYzzsykZQQgn^Udq z!T$ueR@RCd3`-6y{88I~>91LBfS*yL(E^_iU#62S<4|%;SChZ;LRXOiCsK|op1?tc z^F*$M7=gvbQU(xt3bjc{5OtC;l?3BK^qv73k?F#Ic4n+}k>$YP(1xS*ia^&~m&;E> zF-gf44Z7moz@ff~U^DYPtn)kt>pX+tr1rAkk)Q{vJ*B3wO|C!At%3`K)xYBrCG7gt zet3~(7oVA!oMNZPci`c2Ot&`DT}PGf);iD4*L`DD*keTMS^x^U*&FZUT;=7pcP;rr zTz7t9V{z0~o;N(;_s~4;mAIQ4Bcg_P>*O@NBl^6n@MK~0h~&_t-$ekzI?*wt+R%P1 zF8{M-cVl$c;oYf2XJ2i5G2J>cZBg=0`Tz`=hEMC~f1Gp2Cm{SjMp$InKesUx;M;adN zx8Z?4>l>>7AzNr(i7Ma;yTYmTSwQLEZYdg-?y8u z`+vBOx?H0p92-{a9jvkOX~NEa>%K48c^6_>ll?xdWM30P89>8%06S&8oBAHy+|g=l zo71pNYMiD303XvyL_t)FGlS^en1NXH6qT>x7X)_O83|;M8SM?CMq^C_bVK3R4E>pXvEeKRk_w)%(Bmo$#!mX34lUGLgWmz{$} z>UY6YesWYT;kUuKTgGR>$_e-1*x$VfcB$3Na*Z8+{q5WOr%V{rxS20E%lLYc4(~Y{ z>7Q)-pZd#o;KJAWG>{%7OUEhYleq)xzrb_?I))N^IW8leBj8@EV}PPM&Q93oG9^_) zmgLKEee@%_!3<}qLSck4gqt)qnN8WW!{$7&+6MPx zbkHvKp}j9S*J>|5AJ0>Echa!^pH|t-2Uj*r|J7%hT{0$fuIIri<+riU!-q!qjq>+7 zvwye2f#1#E@w_$l%#L5FyS>>$&+IMtV;|mrs`lM2M54w|He^CA*z)fVN1=>#lOfBZ{OB7@FPl}EL=sBwBPi$`iZ zA-N``(PA;hy1bkNJAm>bGoNOq%A#LUm-hwe)kUD=S(*p8#9R8azGi!8Pq$@fPq!H> zH`&>buE(^jg`;wT`j$18apCr*3#|6MbFtq*-NtEp1V7ZAZf$Bm@yNB$vOCaQZ_eI( zUGw@zj&yu7y~l@bHP=7>^2Rkce);wPXtzJU83yufU}1l~P1?4?A9UryGavNiBJ>az z&sKiBnP?Yc+-0w6JFXttxyiDx{0SBs--)|CH#PGpL55~sVY{z+j~%+;M{vUp7P#HA z>6O5OQ_yjcqmjGgB!dqJATJrakXTBzDGMs6cW|%|G$#!VLXns3M4$8^EDsdY$%9eu z;6hIhNgB}s7-wZe7n~)=%b|au^2|H4`S?d;nmPZ7O_~lU7*HSTOJ1H5f5B0gWEA{^ zUy_f$VV%f>hiIU7a15nT&fBkA5`_NV3%ow$rYn_nWBbJ#RR?EGwQc9kvK{~??An`t zSdyUfiLV=d6nDYjy~%3R1}&R2xmYxb%F|UJx`FhLQL(Z=ghPt2mTCJ@vE@Z&Vg_m zZZYC$h`VJUd|AP$oO|;fE39|$f-;Etkpugz=k~wzr}Z0l(He0NPnc)>m%Pb_&V6xS z<>s4;5h>ixp}Ndi#!YiqFcJ>kfDYvj8F)FspnQZ;+a zcX);0PC?W~D?wg@&gIMof=jTcn(gk-2PZ&nN9XCCEF zsEpu)j0~G~`lW`Em)yghN6d&(q}=HM3g;nOofDHcU_tIK9wpY5^aq$VF%?5rsi5bG zK-?3<2%ex}6hQKkjj}BcIS18;-Z?#>

F=%JRLoV68eRoy3QoL#UY(VglRVlg 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 { - const { name } = context.params; - return callback(null, { name }); -}; diff --git a/packages/oc-template-react-compiler/scaffold/src/src/App.test.tsx b/packages/oc-template-react-compiler/scaffold/src/src/App.test.tsx new file mode 100644 index 0000000..529aec0 --- /dev/null +++ b/packages/oc-template-react-compiler/scaffold/src/src/App.test.tsx @@ -0,0 +1,29 @@ +import React from 'react'; +import { describe, it, afterEach, beforeEach, expect, vi } from 'vitest'; +import { render, screen } from '@testing-library/react'; +import { DataProvider } from 'oc-template-typescript-react-compiler/utils/useData'; + +import App from './App'; + +const getData = vi.fn(); + +describe('App - Page', () => { + beforeEach(() => { + window.oc = { events: { on: vi.fn(), fire: vi.fn() } } as any; + }); + afterEach(() => { + vi.restoreAllMocks(); + }); + + it('Gets more data when clicking the button', () => { + getData.mockImplementationOnce(() => Promise.resolve({})); + render( + + + + ); + + const extraInfoButton = screen.getByText(/Get extra information/i); + expect(extraInfoButton).toBeTruthy(); + }); +}); diff --git a/packages/oc-template-react-compiler/scaffold/src/src/App.tsx b/packages/oc-template-react-compiler/scaffold/src/src/App.tsx new file mode 100644 index 0000000..7bdda6b --- /dev/null +++ b/packages/oc-template-react-compiler/scaffold/src/src/App.tsx @@ -0,0 +1,51 @@ +import { useState } from 'react'; +import { useData } from 'oc-template-typescript-react-compiler/utils/useData'; +import styles from './styles.css'; +import type { AdditionalData, ClientProps } from './types'; + +interface AppProps extends ClientProps { + getMoreData?: boolean; +} + +const App: React.FC = () => { + const { firstName, lastName, userId, getData, getSetting } = useData(); + const staticPath = getSetting('staticPath'); + const [additionalData, setAdditionalData] = useState(null); + const [error, setError] = useState(''); + + const fetchMoreData = async () => { + setError(''); + try { + const data = await getData({ userId, getMoreData: true }); + setAdditionalData(data); + } catch (err) { + setError(String(err)); + } + }; + + if (error) { + return

Something wrong happened!
; + } + + return ( +
+ Logo +

+ Hello, {firstName} {lastName} +

+ {additionalData && ( +
+
Age: {additionalData.age}
+
+ Hobbies: {additionalData.hobbies.map((x) => x.toLowerCase()).join(', ')} +
+
+ )} + +
+ ); +}; + +export default App; diff --git a/packages/oc-template-react-compiler/scaffold/src/src/oc-app.d.ts b/packages/oc-template-react-compiler/scaffold/src/src/oc-app.d.ts new file mode 100644 index 0000000..f5feb5e --- /dev/null +++ b/packages/oc-template-react-compiler/scaffold/src/src/oc-app.d.ts @@ -0,0 +1,2 @@ +/// +/// diff --git a/packages/oc-template-react-compiler/scaffold/src/src/server.ts b/packages/oc-template-react-compiler/scaffold/src/src/server.ts new file mode 100644 index 0000000..3678ee2 --- /dev/null +++ b/packages/oc-template-react-compiler/scaffold/src/src/server.ts @@ -0,0 +1,34 @@ +import { Context } from 'oc-template-typescript-react-compiler'; +import { AdditionalData, ClientProps, OcParameters } from './types'; + +const database = [ + { name: 'John Doe', age: 34, hobbies: ['Swimming', 'Basketball'] }, + { name: 'Jane Doe', age: 35, hobbies: ['Running', 'Rugby'] } +]; + +async function getUser(userId: number) { + return database[userId]; +} + +export async function data( + context: Context, + callback: (error: any, data: ClientProps | AdditionalData) => void +) { + const { userId } = context.params; + const user = await getUser(userId); + const shouldGetMoreData = context.params.getMoreData; + const [firstName, lastName] = user.name.split(/\s+/); + + if (shouldGetMoreData) { + return callback(null, { + age: user.age, + hobbies: user.hobbies + }); + } + + return callback(null, { + userId, + firstName, + lastName + }); +} diff --git a/packages/oc-template-react-compiler/scaffold/src/src/setupTests.js b/packages/oc-template-react-compiler/scaffold/src/src/setupTests.js new file mode 100644 index 0000000..7b0828b --- /dev/null +++ b/packages/oc-template-react-compiler/scaffold/src/src/setupTests.js @@ -0,0 +1 @@ +import '@testing-library/jest-dom'; diff --git a/packages/oc-template-react-compiler/scaffold/src/src/styles.css b/packages/oc-template-react-compiler/scaffold/src/src/styles.css new file mode 100644 index 0000000..a50ed9d --- /dev/null +++ b/packages/oc-template-react-compiler/scaffold/src/src/styles.css @@ -0,0 +1,30 @@ +.container { + background-color: #3b246c; + color: #fff; + font-family: sans-serif; + padding: 40px; +} + +.button { + background-color: #a97613; + border: none; + padding: 15px 32px; + text-align: center; + font-size: 16px; + text-decoration: none; + display: inline-block; + color: inherit; + cursor: pointer; +} + +.info { + margin-bottom: 20px; +} + +.block { + margin: 6px 0; +} + +.button:hover { + background-color: #c79535; +} diff --git a/packages/oc-template-react-compiler/scaffold/src/src/types.ts b/packages/oc-template-react-compiler/scaffold/src/src/types.ts new file mode 100644 index 0000000..ac66271 --- /dev/null +++ b/packages/oc-template-react-compiler/scaffold/src/src/types.ts @@ -0,0 +1,15 @@ +export interface OcParameters { + userId: number; + getMoreData?: boolean; +} + +export interface AdditionalData { + hobbies: string[]; + age: number; +} + +export interface ClientProps extends Partial { + userId: number; + firstName: string; + lastName: string; +} diff --git a/packages/oc-template-react-compiler/scaffold/src/styles.css b/packages/oc-template-react-compiler/scaffold/src/styles.css deleted file mode 100644 index 2ece34e..0000000 --- a/packages/oc-template-react-compiler/scaffold/src/styles.css +++ /dev/null @@ -1,4 +0,0 @@ -.special { - background: palevioletred; - color: white; -} \ No newline at end of file diff --git a/packages/oc-template-react-compiler/scaffold/src/tsconfig.json b/packages/oc-template-react-compiler/scaffold/src/tsconfig.json new file mode 100644 index 0000000..bf369f9 --- /dev/null +++ b/packages/oc-template-react-compiler/scaffold/src/tsconfig.json @@ -0,0 +1,26 @@ +{ + "compilerOptions": { + // These are suggested values and will be set when not present in the + // tsconfig.json + // 'parsedValue' matches the output value from ts.parseJsonConfigFileContent() + "target": "es5", + "lib": ["dom", "dom.iterable", "esnext"], + "strict": true, + "allowJs": true, + "skipLibCheck": true, + "esModuleInterop": true, + "allowSyntheticDefaultImports": true, + "forceConsistentCasingInFileNames": true, + "noFallthroughCasesInSwitch": true, + + // These values are required and cannot be changed by the user + // Keep this in sync with the rollup config + "module": "esnext", + "moduleResolution": "node", + "resolveJsonModule": true, + "isolatedModules": true, + "jsx": "react-jsx" + }, + "include": ["src"], + "exclude": ["src/_package"] +} diff --git a/packages/oc-template-react-compiler/scaffold/src/vite.config.ts b/packages/oc-template-react-compiler/scaffold/src/vite.config.ts new file mode 100644 index 0000000..69008b4 --- /dev/null +++ b/packages/oc-template-react-compiler/scaffold/src/vite.config.ts @@ -0,0 +1,9 @@ +import { defineConfig } from 'vite'; + +// https://vitejs.dev/config/ +export default defineConfig({ + // @ts-ignore Missing test property + test: { + environment: 'jsdom' + } +}); diff --git a/packages/oc-template-react-compiler/utils/.eslintrc.js b/packages/oc-template-react-compiler/utils/.eslintrc.js new file mode 100644 index 0000000..902fe70 --- /dev/null +++ b/packages/oc-template-react-compiler/utils/.eslintrc.js @@ -0,0 +1,13 @@ +module.exports = { + env: { + commonjs: false + }, + parserOptions: { + ecmaVersion: 'latest', + sourceType: 'module', + ecmaFeatures: { + jsx: true + } + }, + extends: '../../../.eslintrc.js' +}; diff --git a/packages/oc-template-react-compiler/utils/index.js b/packages/oc-template-react-compiler/utils/index.js deleted file mode 100644 index 2615bd2..0000000 --- a/packages/oc-template-react-compiler/utils/index.js +++ /dev/null @@ -1,4 +0,0 @@ -import withDataProvider from "./withDataProvider"; -import withSettingProvider from "./withSettingProvider"; - -export { withDataProvider, withSettingProvider }; diff --git a/packages/oc-template-react-compiler/utils/useData.tsx b/packages/oc-template-react-compiler/utils/useData.tsx new file mode 100644 index 0000000..44b1e57 --- /dev/null +++ b/packages/oc-template-react-compiler/utils/useData.tsx @@ -0,0 +1,38 @@ +import React from 'react'; + +const DataContext = React.createContext({}); + +type Data = T & { + getData(parameters: T, cb: (err: Error | null, data: any) => void): void; + getSetting(setting: 'name' | 'version' | 'baseUrl' | 'staticPath'): string; +}; + +type PromiseData = T & { + getData(parameters?: Partial): Promise; + getSetting(setting: 'name' | 'version' | 'baseUrl' | 'staticPath'): string; +}; + +export const DataProvider = ({ children, ...props }: any) => { + return {children}; +}; + +export function useData(): PromiseData { + const { + value: { getData, ...rest } + }: { value: Data } = React.useContext(DataContext); + const asyncGetData = React.useCallback((data: I) => { + return new Promise((resolve, reject) => { + // @ts-ignore + getData({ ...rest, ...data }, (err, newData) => { + if (err) { + reject(err); + } else { + resolve(newData); + } + }); + }); + }, []); + + // @ts-ignore + return { getData: asyncGetData, ...rest }; +} diff --git a/packages/oc-template-react-compiler/utils/withDataProvider.js b/packages/oc-template-react-compiler/utils/withDataProvider.js deleted file mode 100644 index b53bbae..0000000 --- a/packages/oc-template-react-compiler/utils/withDataProvider.js +++ /dev/null @@ -1,21 +0,0 @@ -import React from "react"; -import PropTypes from "prop-types"; - -const withDataProvider = BaseComponent => { - const Enhanced = (props, context) => { - const propsWithGetData = { - ...props, - getData: context.getData - }; - - return ; - }; - - Enhanced.contextTypes = { - getData: PropTypes.func - }; - - return Enhanced; -}; - -export default withDataProvider; diff --git a/packages/oc-template-react-compiler/utils/withSettingProvider.js b/packages/oc-template-react-compiler/utils/withSettingProvider.js deleted file mode 100644 index b4fda43..0000000 --- a/packages/oc-template-react-compiler/utils/withSettingProvider.js +++ /dev/null @@ -1,21 +0,0 @@ -import React from "react"; -import PropTypes from "prop-types"; - -const withSettingProvider = BaseComponent => { - const Enhanced = (props, context) => { - const propsWithGetSetting = { - ...props, - getSetting: context.getSetting - }; - - return ; - }; - - Enhanced.contextTypes = { - getSetting: PropTypes.func - }; - - return Enhanced; -}; - -export default withSettingProvider; From 9eff31230ca9d5fec1596f871624b557b7500355 Mon Sep 17 00:00:00 2001 From: Ricardo Devis Agullo Date: Tue, 28 Mar 2023 21:23:26 +0200 Subject: [PATCH 2/2] allow externals to be overriden --- packages/oc-template-react-compiler/lib/compileView.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/oc-template-react-compiler/lib/compileView.js b/packages/oc-template-react-compiler/lib/compileView.js index d63984a..104cacf 100644 --- a/packages/oc-template-react-compiler/lib/compileView.js +++ b/packages/oc-template-react-compiler/lib/compileView.js @@ -45,7 +45,7 @@ async function compileView(options) { const publishFileName = options.publishFileName || 'template.js'; const componentPackage = options.componentPackage; const { getInfo } = require('../index'); - const externals = getInfo().externals; + const externals = componentPackage.oc.files.template.externals || getInfo().externals; const production = !!options.production; const reactOCProviderContent = reactOCProviderTemplate({ viewPath });