diff --git a/.github/workflows/auto-publisher.yml b/.github/workflows/auto-publisher.yml index 7635ec5f1f..f10a372479 100644 --- a/.github/workflows/auto-publisher.yml +++ b/.github/workflows/auto-publisher.yml @@ -12,7 +12,7 @@ jobs: - uses: actions/checkout@v1 - uses: actions/setup-node@v1 with: - node-version: 10 + node-version: 12 registry-url: https://registry.npmjs.org/ - run: npm install - run: npm run setup diff --git a/.github/workflows/bench.yml b/.github/workflows/bench.yml index 124e6ccf97..6e6b1d97f2 100644 --- a/.github/workflows/bench.yml +++ b/.github/workflows/bench.yml @@ -5,7 +5,7 @@ on: branches: - master pull_request: - brenches: + branches: - '^bench' jobs: diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7d6429b2bf..08081c68fe 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -7,7 +7,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - node-version: [10.x] + node-version: [12.x] steps: - uses: actions/checkout@v2 - name: Set branch name diff --git a/.gitignore b/.gitignore index 5c4898e47d..758eb52661 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,4 @@ coverage/ node_modules/ examples/test .eslintcache +*.lock diff --git a/babel.config.js b/babel.config.js index 86100d5593..cc0b48c711 100644 --- a/babel.config.js +++ b/babel.config.js @@ -17,9 +17,19 @@ module.exports = function(api) { ], 'plugins': [ '@babel/plugin-proposal-export-default-from', - ['@babel/plugin-proposal-class-properties', { 'loose': false }], + [ + '@babel/plugin-proposal-class-properties', + { + loose: true + } + ], 'babel-plugin-transform-jsx-stylesheet', - ['@babel/plugin-proposal-decorators', { 'legacy': true }], + [ + '@babel/plugin-proposal-decorators', + { + decoratorsBeforeExport: true + } + ], '@babel/plugin-syntax-dynamic-import', ], 'ignore': [ diff --git a/lerna.json b/lerna.json index ad0ec1558d..1b277f7548 100644 --- a/lerna.json +++ b/lerna.json @@ -1,20 +1,15 @@ { - "version": "1.0.0", - "packages": [ - "packages/*" - ], + "version": "1.7.0", + "npmClient": "ayarn", + "useWorkspaces": true, "command": { "bootstrap": { - "npmClientArgs": [ - "--no-package-lock" - ] - }, - "publish": { - "skipGit": true, - "allowBranch": "master", - "ignoreChanges": [ - "*.md" - ] + "npmClientArgs": ["--no-lockfile"] } - } + }, + "packages": [ + "packages/*", + "scripts/bench" + ] } + diff --git a/package.json b/package.json index f8f22b3228..e4916bf7e4 100644 --- a/package.json +++ b/package.json @@ -1,9 +1,5 @@ { "private": true, - "devEngines": { - "node": "8.x || 9.x || 10.x || 11.x", - "npm": "6.x" - }, "devDependencies": { "@babel/core": "^7.12.9", "@babel/plugin-proposal-class-properties": "^7.2.0", @@ -15,13 +11,12 @@ "@babel/preset-flow": "^7.0.0", "@babel/preset-react": "^7.0.0", "@rollup/plugin-babel": "^5.2.1", - "@rollup/plugin-commonjs": "^16.0.0", - "@rollup/plugin-node-resolve": "^10.0.0", + "@rollup/plugin-commonjs": "^20.0.0", + "@rollup/plugin-node-resolve": "^13.0.0", "@rollup/plugin-replace": "^2.3.4", - "@rollup/plugin-virtual": "^2.0.3", "@typescript-eslint/eslint-plugin": "^4.8.2", "@typescript-eslint/parser": "^4.8.2", - "axios": "^0.21.0", + "axios": "^0.21.1", "babel-eslint": "^10.0.3", "babel-jest": "^26.6.3", "babel-loader": "^8.0.4", @@ -36,12 +31,11 @@ "eslint-config-rax": "^0.1.0", "eslint-plugin-import": "^2.22.1", "eslint-plugin-module": "^0.1.0", - "eslint-plugin-react": "^7.21.5", + "eslint-plugin-react": "7.24.0", "findup": "^0.1.5", "ghooks": "^2.0.4", "glob": "^7.1.3", "gzip-size": "^5.0.0", - "image-source-loader": "^0.6.5", "istanbul-api": "^2.0.6", "istanbul-lib-coverage": "^2.0.1", "jest": "^26.6.3", @@ -51,21 +45,16 @@ "lerna": "^3.16.4", "minimatch": "^3.0.4", "minimist": "^1.2.0", - "rax-webpack-plugin": "^0.6.5", - "rimraf": "^2.6.2", "rollup": "^2.33.3", "rollup-plugin-terser": "^7.0.2", "semver": "^7.1.0", "semver-regex": "^2.0.0", - "stylesheet-loader": "^0.6.5", - "terser-webpack-plugin": "^5.0.3", - "typescript": "^3.7.5", + "typescript": "^4.0.0", "uppercamelcase": "^3.0.0", "webpack": "^4.27.1" }, "scripts": { - "bootstrap": "npm run clean && lerna bootstrap --no-ci && npm run build", - "setup": "rm -rf node_modules && npm install --registry=https://registry.npm.taobao.org/ && npm run bootstrap", + "setup": "rm -rf node_modules && yarn install && npm run clean && npm run build", "build:compile": "npm run clean:compile && npm run build:compile:packages", "build:compile:packages": "node ./scripts/compile-packages.js", "build:dist": "npm run clean:dist && node ./scripts/dist-core.js", @@ -77,9 +66,8 @@ "lint:fix": "eslint --cache --ext .js,.jsx --fix ./", "coverage": "npm run test -- --coverage", "coverage:upload": "npm run clean:coverage && npm run coverage && node ./scripts/mapCoverage.js && codecov", - "publish": "node ./scripts/publish.js", "clean:dist": "rm -rf ./packages/*/dist", - "clean:compile": "rm -rf ./packages/*/lib", + "clean:compile": "rm -rf ./packages/*/lib && rm -rf ./packages/*/es", "clean:coverage": "rm -rf ./coverage", "clean:modules": "rm -rf ./packages/*/node_modules", "clean": "jest --clearCache && lerna clean --yes && npm run clean:modules && npm run clean:compile && npm run clean:dist && npm run clean:coverage", @@ -89,10 +77,15 @@ }, "config": { "ghooks": { - "commit-msg": "./scripts/validate-commit-msg.js" + "commit-msg": "./scripts/validate-commit-msg.js", + "pre-commit": "npm run lint" }, "commitizen": { "path": "./node_modules/cz-conventional-changelog" } - } + }, + "workspaces": [ + "packages/*", + "scripts/*" + ] } diff --git a/packages/rax/CHANGELOG.md b/packages/rax/CHANGELOG.md new file mode 100644 index 0000000000..ce3efd6e3e --- /dev/null +++ b/packages/rax/CHANGELOG.md @@ -0,0 +1,7 @@ +## CHANGELOG + +### 1.2.1 + +- refactor: change output result format, avoid mixing `IIFE` and `CJS` +- chore: avoid throw error when update unmounted component +- fix: add error message to `getDerivedStateFromError` hook diff --git a/packages/rax/index.js b/packages/rax/index.js index 5123a4d009..33b539ef2e 100644 --- a/packages/rax/index.js +++ b/packages/rax/index.js @@ -1,5 +1,5 @@ if (process.env.NODE_ENV === 'production') { module.exports = require('./dist/rax.min.js'); } else { - module.exports = require('./lib/index.js'); + module.exports = require('./dist/rax.js'); } diff --git a/packages/rax/package.json b/packages/rax/package.json index 7d41a505c6..07aa8d3966 100644 --- a/packages/rax/package.json +++ b/packages/rax/package.json @@ -1,6 +1,6 @@ { "name": "rax", - "version": "1.2.0", + "version": "1.2.1", "description": "A universal React-compatible render engine.", "license": "BSD-3-Clause", "main": "index.js", diff --git a/packages/rax/src/__tests__/asyncUpdate.js b/packages/rax/src/__tests__/asyncUpdate.js new file mode 100644 index 0000000000..aa76ef5ee7 --- /dev/null +++ b/packages/rax/src/__tests__/asyncUpdate.js @@ -0,0 +1,115 @@ +/* @jsx createElement */ + +import createElement from '../createElement'; +import Component from '../vdom/component'; +import render from '../render'; +import Host from '../vdom/host'; +import ServerDriver from 'driver-server'; +import { useState, useEffect } from '../hooks'; + +describe('update unmounted component', () => { + function createNodeElement(tagName) { + return { + nodeType: 1, + tagName: tagName.toUpperCase(), + attributes: {}, + style: {}, + childNodes: [], + parentNode: null + }; + } + + beforeEach(function() { + Host.driver = ServerDriver; + jest.useFakeTimers(); + }); + + afterEach(function() { + Host.driver = null; + jest.useRealTimers(); + }); + + it('should warn about class component', () => { + const container = createNodeElement('div'); + let destroyChild; + + class Child extends Component { + state = { + name: 'hello' + } + componentDidMount() { + setTimeout(() => { + this.setState({ + name: 'work' + }); + }, 1000); + } + render() { + return
{this.state.name}
; + } + } + + class App extends Component { + state = { + showChild: true + } + componentDidMount() { + destroyChild = () => { + this.setState({ + showChild: false + }); + }; + } + render() { + return (
+ { this.state.showChild ? : null } +
); + } + } + + expect(() => { + render(, container); + destroyChild(); + jest.runAllTimers(); + }).toWarnDev("Warning: Can't perform a Rax state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in the componentWillUnmount method.", { withoutStack: true }); + }); + + it('should warn about function component', () => { + const container = createNodeElement('div'); + let destroyChild; + + function Child() { + const [name, setName] = useState('hello'); + useEffect(() => { + setTimeout(() => { + setName('world'); + }, 1000); + }, []); + return
{name}
; + } + + class App extends Component { + state = { + showChild: true + } + componentDidMount() { + destroyChild = () => { + this.setState({ + showChild: false + }); + }; + } + render() { + return (
+ { this.state.showChild ? : null } +
); + } + } + + expect(() => { + render(, container); + destroyChild(); + jest.runAllTimers(); + }).toWarnDev("Warning: Can't perform a Rax state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in a useEffect cleanup function.", { withoutStack: true }); + }); +}); diff --git a/packages/rax/src/__tests__/createContext.js b/packages/rax/src/__tests__/createContext.js index cdc78b36a3..b316db86c2 100644 --- a/packages/rax/src/__tests__/createContext.js +++ b/packages/rax/src/__tests__/createContext.js @@ -7,7 +7,7 @@ import render from '../render'; import ServerDriver from 'driver-server'; import createContext from '../createContext'; import createRef from '../createRef'; -import {useState} from '../hooks'; +import { useState } from '../hooks'; describe('createContext', () => { function createNodeElement(tagName) { diff --git a/packages/rax/src/compat/index.js b/packages/rax/src/compat/index.js index 2ad98144b7..42d40a56fb 100644 --- a/packages/rax/src/compat/index.js +++ b/packages/rax/src/compat/index.js @@ -1,9 +1,11 @@ -import * as Rax from '../../index'; +import * as RaxCore from '../../index'; import Children from 'rax-children'; import isValidElement from 'rax-is-valid-element'; import createFactory from 'rax-create-factory'; import cloneElement from 'rax-clone-element'; +const Rax = RaxCore; + Rax.Children = Children; Rax.isValidElement = isValidElement; Rax.createFactory = createFactory; diff --git a/packages/rax/src/vdom/__tests__/composite.js b/packages/rax/src/vdom/__tests__/composite.js index c1db468786..a29fe42e1c 100644 --- a/packages/rax/src/vdom/__tests__/composite.js +++ b/packages/rax/src/vdom/__tests__/composite.js @@ -762,6 +762,46 @@ describe('CompositeComponent', function() { expect(container.childNodes[0].childNodes[0].data).toBe('Something went wrong.'); }); + it('should catch the exact error with getDerivedStateFromError.', () => { + let caughtError; + let exampleError = new Error('Example error message'); + let container = createNodeElement('div'); + + class ErrorBoundary extends Component { + constructor(props) { + super(props); + this.state = { hasError: false, error: null }; + } + + static getDerivedStateFromError(error) { + caughtError = error; + return { hasError: true, error }; + } + + render() { + if (this.state.hasError) { + return

{this.state.error.message}

; + } + + return this.props.children; + } + } + + function BrokenRender(props) { + throw exampleError; + } + + render( + + + , container); + + jest.runAllTimers(); + + expect(caughtError).toBe(exampleError); + expect(container.childNodes[0].childNodes[0].data).toBe('Example error message'); + }); + it('should render correct when prevRenderedComponent did not generate nodes', () => { let container = createNodeElement('div'); class Frag extends Component { diff --git a/packages/rax/src/vdom/performInSandbox.js b/packages/rax/src/vdom/performInSandbox.js index fd15cb3aff..fda3b71ebc 100644 --- a/packages/rax/src/vdom/performInSandbox.js +++ b/packages/rax/src/vdom/performInSandbox.js @@ -39,7 +39,7 @@ export function handleError(instance, error) { // Update state to the next render to show the fallback UI. if (boundary.constructor && boundary.constructor.getDerivedStateFromError) { - const state = boundary.constructor.getDerivedStateFromError(); + const state = boundary.constructor.getDerivedStateFromError(error); boundary.setState(state); } }, boundaryInternal.__parentInstance); diff --git a/packages/rax/src/vdom/reactive.js b/packages/rax/src/vdom/reactive.js index 572bcd9f36..800577c50e 100644 --- a/packages/rax/src/vdom/reactive.js +++ b/packages/rax/src/vdom/reactive.js @@ -110,7 +110,6 @@ export default class ReactiveComponent extends Component { } __update() { - this[INTERNAL].__isPendingForceUpdate = true; this.setState(EMPTY_OBJECT); } diff --git a/packages/rax/src/vdom/updater.js b/packages/rax/src/vdom/updater.js index e3003ccfa2..62088d40f5 100644 --- a/packages/rax/src/vdom/updater.js +++ b/packages/rax/src/vdom/updater.js @@ -106,6 +106,18 @@ function requestUpdate(component, partialState, callback) { let internal = component[INTERNAL]; if (!internal) { + if (process.env.NODE_ENV !== 'production') { + // Block other render + Host.__isUpdating = false; + console.error( + "Warning: Can't perform a Rax state update on an unmounted component. This " + + 'is a no-op, but it indicates a memory leak in your application. To ' + + 'fix, cancel all subscriptions and asynchronous tasks in %s.', + component.__isReactiveComponent + ? 'a useEffect cleanup function' + : 'the componentWillUnmount method', + ); + } return; } @@ -117,6 +129,10 @@ function requestUpdate(component, partialState, callback) { // setState if (partialState) { + // Function Component should force update + if (component.__isReactiveComponent) { + internal.__isPendingForceUpdate = true; + } enqueueState(internal, partialState); // State pending when request update in componentWillMount and componentWillReceiveProps, // isPendingState default is false value (false or null) and set to true after componentWillReceiveProps, diff --git a/packages/rax/src/version.js b/packages/rax/src/version.js index d3c25c7e42..208e8b7c49 100644 --- a/packages/rax/src/version.js +++ b/packages/rax/src/version.js @@ -1 +1 @@ -export default '1.1.0'; +export default process.env.RAX_VERSION; diff --git a/scripts/bench/frameworks/rax-local/src/main.jsx b/scripts/bench/frameworks/rax-local/src/main.jsx index 6c7b10485b..9905b96dac 100755 --- a/scripts/bench/frameworks/rax-local/src/main.jsx +++ b/scripts/bench/frameworks/rax-local/src/main.jsx @@ -111,4 +111,4 @@ const Main = () => { ); }; -render(
, document.getElementById('main'), { driver: DriverDOM }); \ No newline at end of file +render(
, document.getElementById('main'), { driver: DriverDOM }); diff --git a/scripts/compile.js b/scripts/compile.js index 7c22b5c6db..07bc07c786 100644 --- a/scripts/compile.js +++ b/scripts/compile.js @@ -25,6 +25,9 @@ const SRC_DIR = 'src'; const JS_FILES_PATTERN = '**/*.js'; const IGNORE_PATTERN = '**/{__tests__,__mocks__}/**'; +// Don't need compile packages +const IGNORE_COMPILE_PACKAGES = ['rax']; + const args = parseArgs(process.argv); const customPackages = args.packages; @@ -56,6 +59,7 @@ function buildPackage(packagesDir, p, isBuildEs) { function getPackages(packagesDir, customPackages) { return fs.readdirSync(packagesDir) + .filter(file => !IGNORE_COMPILE_PACKAGES.includes(file)) // Exclude compile rax .map(file => path.resolve(packagesDir, file)) .filter(f => { if (customPackages) { diff --git a/scripts/dist-core.js b/scripts/dist-core.js index 90fb5534f3..2d0f6d9bd4 100755 --- a/scripts/dist-core.js +++ b/scripts/dist-core.js @@ -1,7 +1,7 @@ +const { join } = require('path'); +const { readFileSync, existsSync } = require('fs'); const { green } = require('chalk'); -const { basename } = require('path'); const rollup = require('rollup'); -const virtual = require('@rollup/plugin-virtual'); const { nodeResolve } = require('@rollup/plugin-node-resolve'); const commonjs = require('@rollup/plugin-commonjs'); const { babel } = require('@rollup/plugin-babel'); @@ -9,19 +9,9 @@ const { terser } = require('rollup-plugin-terser'); const replace = require('@rollup/plugin-replace'); const gzipSize = require('gzip-size'); -const IIFE = 'iife'; const UMD = 'umd'; const ESM = 'esm'; - -function transformBundleFormat({ input, name, format, entry, shouldExportDefault }) { - return format === IIFE ? virtual({ - entry: ` - import ${shouldExportDefault ? name : `* as ${name}`} from './${basename(entry)}'; - if (typeof module !== 'undefined') module.exports = ${name}; - else self.${name} = ${name}; - ` - }) : null; -} +const CJS = 'cjs'; function getExtension(format) { let ext = '.js'; @@ -34,15 +24,37 @@ function getExtension(format) { return ext; } -async function build({ package: packageName, entry = 'src/index.js', name, shouldMinify = false, format = UMD, shouldExportDefault = false }) { +/** + * Rollup build options + * @param buildOptions - rollup build options. + * @param {string} buildOptions.packageName - package name + * @param {string} buildOptions.name - package export name, such as umd format: window.Rax + * @param {string} buildOptions.entry package entry path + * @param {string} buildOptions.outputPath package output path + * @param {boolean} buildOptions.shouldMinify compress code or not + * @param {string} buildOptions.format bundle format (cjs|esm|iife|umd) + * @param {Array} buildOptions.external external dependencies list + * @param {Object} buildOptions.replaceValues rollup replace plugin values + */ +async function build({ + packageName, + name, + entry = 'src/index.js', + outputPath, + shouldMinify = false, + format = UMD, + external, + replaceValues, +}) { const input = `./packages/${packageName}/${entry}`; const output = { name, exports: 'named', - sourcemap: true, + sourcemap: 'hidden', compact: false, // This will minify the wrapper code generated by rollup. freeze: false, strict: false, + esModule: format === ESM, }; const terserOptions = { @@ -66,12 +78,12 @@ async function build({ package: packageName, entry = 'src/index.js', name, shoul // For development const bundle = await rollup.rollup({ input, + external, plugins: [ - transformBundleFormat({ input, name, format, entry, shouldExportDefault }), nodeResolve(), commonjs({ // style-unit for build while packages linked - // use /pakacges/ would get error and it seemed to be a rollup-plugin-commonjs bug + // use /packages/ would get error and it seemed to be a rollup-plugin-commonjs bug include: /node_modules|style-unit/ }), babel({ @@ -89,6 +101,7 @@ async function build({ package: packageName, entry = 'src/index.js', name, shoul }), replace({ values: { + ...replaceValues, 'process.env.NODE_ENV': JSON.stringify(shouldMinify ? 'production' : 'development'), }, preventAssignment: true, @@ -97,8 +110,10 @@ async function build({ package: packageName, entry = 'src/index.js', name, shoul ] }); + const ext = getExtension(format); + if (shouldMinify) { - const file = `./packages/${packageName}/dist/${packageName}.min.js`; + const file = outputPath || `./packages/${packageName}/dist/${packageName}.min${ext}`; await bundle.write({ ...output, format, @@ -111,23 +126,62 @@ async function build({ package: packageName, entry = 'src/index.js', name, shoul console.log(file, `${green((size / 1024).toPrecision(8) + 'KiB')} (Gzipped)`); } else { - const ext = getExtension(format); await bundle.write({ ...output, format, - file: `./packages/${packageName}/dist/${packageName}${ext}`, + file: outputPath || `./packages/${packageName}/dist/${packageName}${ext}`, }); } } -function buildCorePackages(packageName, name) { - build({ package: packageName, name: name }); - build({ package: packageName, name: name, format: IIFE }); - build({ package: packageName, name: name, format: IIFE, shouldMinify: true }); - build({ package: packageName, name: name, format: ESM }); +function buildCorePackages(options) { + build(options); + build({ ...options, shouldMinify: true }); + build({ ...options, format: CJS }); + build({ ...options, format: CJS, shouldMinify: true }); + build({ ...options, format: ESM }); + build({ ...options, format: ESM, shouldMinify: true }); } -buildCorePackages('rax', 'Rax'); -buildCorePackages('driver-dom', 'DriverDOM'); -buildCorePackages('driver-kraken', 'DriverKraken'); -buildCorePackages('driver-weex', 'DriverWeex'); +// Get rax version +function getRaxVersion() { + const packageJSONPath = join(process.cwd(), 'packages/rax/package.json'); + if (!existsSync(packageJSONPath)) { + throw new Error('rax package.json is not exists!'); + } + const packageData = JSON.parse(readFileSync(packageJSONPath, { encoding: 'utf-8' })); + return JSON.stringify(packageData.version); +} + +const raxVersion = getRaxVersion(); + +buildCorePackages({ + packageName: 'rax', + name: 'Rax', + replaceValues: { + 'process.env.RAX_VERSION': raxVersion + } +}); +buildCorePackages({ + packageName: 'driver-dom', + name: 'DriverDom' +}); +buildCorePackages({ + packageName: 'driver-kraken', + name: 'DriverKraken' +}); +buildCorePackages({ + packageName: 'driver-weex', + name: 'DriverKraken' +}); + +// Build rax compat react version to rax/lib/compat/index.js +build({ + packageName: 'rax', + name: 'Rax', + entry: 'src/compat/index.js', + outputPath: './packages/rax/lib/compat/index.js', + format: CJS, + external: ['rax-children', 'rax-is-valid-element', 'rax-create-factory', 'rax-clone-element'], + replaceValues: { 'process.env.RAX_VERSION': raxVersion }, +}); diff --git a/scripts/jest/toWarnDev.js b/scripts/jest/toWarnDev.js index 6fbbb94d9f..0aec381f40 100644 --- a/scripts/jest/toWarnDev.js +++ b/scripts/jest/toWarnDev.js @@ -1,5 +1,5 @@ // https://github.com/facebook/react/blob/master/scripts/jest/matchers/toWarnDev.js -const jestDiff = require('jest-diff'); // eslint-disable-line import/no-extraneous-dependencies +const jestDiff = require('jest-diff').default; // eslint-disable-line import/no-extraneous-dependencies const util = require('util'); const shouldIgnoreConsoleError = require('./shouldIgnoreConsoleError'); diff --git a/scripts/publish.js b/scripts/publish.js deleted file mode 100644 index bbbf426ecb..0000000000 --- a/scripts/publish.js +++ /dev/null @@ -1,95 +0,0 @@ -/** - * Example: - * node ./scripts/publish.js 1.0.0 --force-publish=* - */ -'use strict'; - -const fs = require('fs'); -const path = require('path'); -const execSync = require('child_process').execSync; -const spawnSync = require('child_process').spawnSync; - -const version = process.argv[2]; -const forcePublish = process.argv[3] || ''; -const EXAMPLES_DIR = path.resolve(__dirname, '../examples'); - -function getExamplesHtmlEntry() { - let entry = []; - - fs.readdirSync(EXAMPLES_DIR) - .forEach(file => { - let f = path.resolve(EXAMPLES_DIR, file, 'public/index.html'); - if (fs.existsSync(f)) { - entry.push(f); - } - }); - - return entry; -} - -if (version) { - const semver = '^' + version; - - console.log('Update rax version file to', version); - const RAX_VERSION_FILE = 'packages/rax/src/version.js'; - fs.writeFileSync(RAX_VERSION_FILE, 'export default \'' + version + '\';\n'); - - console.log('Update dependencies in package.json'); - // update rax-cli depenpencies - begin - const GENERATOR_DEPENDENCIES_FILE = path.resolve(__dirname, '../packages/rax-cli/src/generator/templates/package.json'); - const JSONString = fs.readFileSync(GENERATOR_DEPENDENCIES_FILE); - const packageJSON = JSON.parse(JSONString); - packageJSON.dependencies.rax = semver; - packageJSON.dependencies['rax-view'] = semver; - packageJSON.dependencies['rax-text'] = semver; - packageJSON.devDependencies['rax-scripts'] = semver; - - fs.writeFileSync(GENERATOR_DEPENDENCIES_FILE, JSON.stringify(packageJSON, null, ' ')); - console.log('*', GENERATOR_DEPENDENCIES_FILE); - // update rax-cli depenpencies - end - - // update rax-scripts dependencies - begin - const RAX_SCRIPTS_DEPENDENCIES_FILE = path.resolve(__dirname, '../packages/rax-scripts/package.json'); - const raxScriptPackageJSONString = fs.readFileSync(RAX_SCRIPTS_DEPENDENCIES_FILE); - const raxScritpsPackageJSON = JSON.parse(raxScriptPackageJSONString); - raxScritpsPackageJSON.dependencies['babel-preset-rax'] = semver; - raxScritpsPackageJSON.dependencies['image-source-loader'] = semver; - raxScritpsPackageJSON.dependencies['rax-hot-loader'] = semver; - raxScritpsPackageJSON.dependencies['rax-hot-module-replacement-webpack-plugin'] = semver; - raxScritpsPackageJSON.dependencies['rax-webpack-plugin'] = semver; - raxScritpsPackageJSON.dependencies['stylesheet-loader'] = semver; - - fs.writeFileSync(RAX_SCRIPTS_DEPENDENCIES_FILE, JSON.stringify(raxScritpsPackageJSON, null, ' ')); - console.log('*', RAX_SCRIPTS_DEPENDENCIES_FILE); - // update rax-scripts dependencies - end - - const PROJECT_DEPENDENCIES_FILE = path.resolve(__dirname, '../package.json'); - const ProjectPackageJSON = JSON.parse(fs.readFileSync(PROJECT_DEPENDENCIES_FILE)); - ProjectPackageJSON.devDependencies['babel-preset-rax'] = semver; - ProjectPackageJSON.devDependencies['rax-webpack-plugin'] = semver; - ProjectPackageJSON.devDependencies['stylesheet-loader'] = semver; - - fs.writeFileSync(PROJECT_DEPENDENCIES_FILE, JSON.stringify(ProjectPackageJSON, null, ' ')); - console.log('*', PROJECT_DEPENDENCIES_FILE); - - console.log('Update rax-web-framework version in index.html'); - const GENERATOR_HTML_FILE = path.resolve(__dirname, '../packages/rax-cli/src/generator/templates/public/index.html'); - - const examplesHtmlEntry = getExamplesHtmlEntry(); - const htmlEntry = examplesHtmlEntry.concat(GENERATOR_HTML_FILE); - htmlEntry.forEach(function(f) { - console.log('*', f); - const HTMLString = String(fs.readFileSync(f)); - const updatedHTMLString = HTMLString.replace(/web-rax-framework@\d*.\d*.\d*/g, 'web-rax-framework@' + version); - fs.writeFileSync(f, updatedHTMLString); - }); - - execSync( - 'npm run build && npm run lint && npm run test &&' + - 'lerna publish --skip-git --repo-version=' + version + ' ' + forcePublish, { - stdio: 'inherit' - } - ); -} else { - console.log('Must specific publish version like: npm run publish 9.9.9'); -}