mirror of https://github.com/alibaba/ice.git
parent
105a496f68
commit
974e0a0152
|
@ -7,9 +7,13 @@ const commonRules = {
|
|||
"no-param-reassign": 0,
|
||||
"comma-dangle": 0,
|
||||
"prefer-object-spread": 0,
|
||||
"import/named": 0,
|
||||
// TODO: open rule indent, consider of MemberExpression
|
||||
"indent": 0,
|
||||
'semi': 2,
|
||||
"semi": 2,
|
||||
"react/react-in-jsx-scope": 0,
|
||||
"jsx-a11y/html-has-lang": 0,
|
||||
"react/static-property-placement": 0
|
||||
};
|
||||
|
||||
const jsRules = deepmerge(eslint, {
|
||||
|
|
|
@ -14,6 +14,7 @@ packages/*/lib/
|
|||
|
||||
# temp folder .ice
|
||||
examples/*/.ice
|
||||
examples/*/.rax
|
||||
|
||||
.eslintcache
|
||||
docs/.vuepress/dist/
|
||||
|
|
|
@ -298,22 +298,24 @@ icejs 中一般不允许修改该配置。
|
|||
|
||||
注意,devServer 不支持 port 属性配置,如需改变端口,请通过命令行参数传入。
|
||||
|
||||
### targets
|
||||
### browserslist
|
||||
|
||||
- 类型: `string` | `object`
|
||||
- 默认值:`last 2 versions, Firefox ESR, > 1%, ie >= 9, iOS >= 8, Android >= 4`
|
||||
|
||||
配置 @babel/preset-env 的 [targets](https://babeljs.io/docs/en/babel-preset-env#targets),配置浏览器最低版本,新配置的 `targets` 会覆盖默认值。
|
||||
配置 @babel/preset-env 的浏览器最低版本(https://babeljs.io/docs/en/babel-preset-env#targets),新配置的 `browserslist` 会覆盖默认值。
|
||||
|
||||
```json
|
||||
{
|
||||
"targets": {
|
||||
"browserslist": {
|
||||
"chrome": 49,
|
||||
"ie": 11,
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
> 注: 因 targets 字段被使用,这里使用 browserslist 字段替代 @babel/preset-env 的 targets 字段。
|
||||
|
||||
### vendor
|
||||
|
||||
- 类型:`boolean`
|
||||
|
|
|
@ -5,7 +5,13 @@ const appConfig: IAppConfig = {
|
|||
app: {
|
||||
rootId: 'ice-container',
|
||||
errorBoundary: true,
|
||||
parseSearchParams: true
|
||||
parseSearchParams: true,
|
||||
onShow() {
|
||||
console.log('app show...');
|
||||
},
|
||||
onHide() {
|
||||
console.log('app hide...');
|
||||
},
|
||||
},
|
||||
logger: {
|
||||
level: APP_MODE === 'build' ? 'error' : 'debug',
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import React from 'react';
|
||||
import { Link, helpers, logger, config } from 'ice';
|
||||
import { Link, usePageShow, usePageHide, helpers, logger, config } from 'ice';
|
||||
|
||||
logger.debug('helpers from ice', helpers.urlParse);
|
||||
logger.debug('logger from ice', logger.debug);
|
||||
|
@ -15,6 +15,14 @@ export default function Home(props) {
|
|||
logger.info('Home props', props);
|
||||
logger.info('render home config.appId', config.appId);
|
||||
|
||||
usePageShow(() => {
|
||||
console.log('page show....');
|
||||
});
|
||||
|
||||
usePageHide(() => {
|
||||
console.log('page hide...');
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
<h2>Home Page...{props.count}</h2>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import React from 'react';
|
||||
import { Link } from 'ice';
|
||||
|
||||
const Home = (props) => {
|
||||
const NotFound = (props) => {
|
||||
console.log('render 404', props);
|
||||
|
||||
return (
|
||||
|
@ -14,4 +14,4 @@ const Home = (props) => {
|
|||
);
|
||||
};
|
||||
|
||||
export default Home;
|
||||
export default NotFound;
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
"name": "example-hello-world",
|
||||
"description": "hello world",
|
||||
"dependencies": {
|
||||
"ice.js": "^1.0.0",
|
||||
"ice.js": "1.7.0-7",
|
||||
"react": "^16.4.1",
|
||||
"react-dom": "^16.4.1"
|
||||
},
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
# with rax web
|
||||
|
||||
https://github.com/ice-lab/icejs/tree/master/examples
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"plugins": [],
|
||||
"targets": ["miniapp"]
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
{
|
||||
"name": "with-rax-web",
|
||||
"description": "rax web example",
|
||||
"dependencies": {
|
||||
"rax": "^1.1.0",
|
||||
"rax-app": "^2.0.0",
|
||||
"rax-document": "^0.1.0",
|
||||
"rax-image": "^2.0.0",
|
||||
"rax-link": "^1.0.1",
|
||||
"rax-text": "^1.0.0",
|
||||
"rax-view": "^1.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"miniapp-render": "1.2.0-0",
|
||||
"@types/rax": "^1.0.0"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "raxjs start",
|
||||
"build": "raxjs build"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8.0.0"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
{
|
||||
"routes": [
|
||||
{
|
||||
"path": "/",
|
||||
"source": "pages/Home/index"
|
||||
},
|
||||
{
|
||||
"path": "/about",
|
||||
"source": "pages/About/index"
|
||||
}
|
||||
],
|
||||
"window": {
|
||||
"title": "Rax App"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
import { runApp } from 'rax-app';
|
||||
|
||||
runApp({
|
||||
app: {
|
||||
onShow() {
|
||||
console.log('app show...');
|
||||
},
|
||||
onHide() {
|
||||
console.log('app hide...');
|
||||
}
|
||||
}
|
||||
});
|
|
@ -0,0 +1,5 @@
|
|||
.logo {
|
||||
width: 200rpx;
|
||||
height: 180rpx;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
import { createElement } from 'rax';
|
||||
import Image from 'rax-image';
|
||||
|
||||
import './index.css';
|
||||
|
||||
export default () => {
|
||||
const source = {
|
||||
uri: '//gw.alicdn.com/tfs/TB1MRC_cvb2gK0jSZK9XXaEgFXa-1701-1535.png',
|
||||
};
|
||||
return (
|
||||
<Image
|
||||
className="logo"
|
||||
source={source}
|
||||
/>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,21 @@
|
|||
import { createElement } from 'rax';
|
||||
import { Root, Style, Script } from 'rax-document';
|
||||
|
||||
function Document() {
|
||||
return (
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1,minimum-scale=1,user-scalable=no,viewport-fit=cover" />
|
||||
<title>@ali/demo-app</title>
|
||||
<Style />
|
||||
</head>
|
||||
<body>
|
||||
{/* root container */}
|
||||
<Root />
|
||||
<Script />
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
export default Document;
|
|
@ -0,0 +1,16 @@
|
|||
.about {
|
||||
align-items: center;
|
||||
margin-top: 200rpx;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 45rpx;
|
||||
font-weight: bold;
|
||||
margin: 20rpx 0;
|
||||
}
|
||||
|
||||
.info {
|
||||
font-size: 36rpx;
|
||||
margin: 8rpx 0;
|
||||
color: #555;
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
import { createElement, PureComponent } from 'rax';
|
||||
import View from 'rax-view';
|
||||
import Text from 'rax-text';
|
||||
import { withPageLifeCycle } from 'rax-app';
|
||||
|
||||
import './index.css';
|
||||
|
||||
class About extends PureComponent {
|
||||
public onShow() {
|
||||
console.log('about show...');
|
||||
}
|
||||
|
||||
public onHide() {
|
||||
console.log('about hide...');
|
||||
}
|
||||
|
||||
public render() {
|
||||
return (
|
||||
<View className="about">
|
||||
<Text className="title">About Page!!!</Text>
|
||||
<Text className="info" onClick={() => this.props.history.push('/')}>Go Home</Text>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default withPageLifeCycle(About);
|
|
@ -0,0 +1,16 @@
|
|||
.home {
|
||||
align-items: center;
|
||||
margin-top: 200rpx;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 45rpx;
|
||||
font-weight: bold;
|
||||
margin: 20rpx 0;
|
||||
}
|
||||
|
||||
.info {
|
||||
font-size: 36rpx;
|
||||
margin: 8rpx 0;
|
||||
color: #555;
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
import { createElement } from 'rax';
|
||||
import { usePageShow, usePageHide } from 'rax-app';
|
||||
import View from 'rax-view';
|
||||
import Text from 'rax-text';
|
||||
import Logo from '@/components/Logo';
|
||||
|
||||
import './index.css';
|
||||
|
||||
export default function Home(props) {
|
||||
const { history } = props;
|
||||
|
||||
usePageShow(() => {
|
||||
console.log('home show...');
|
||||
});
|
||||
|
||||
usePageHide(() => {
|
||||
console.log('home hide...');
|
||||
});
|
||||
|
||||
return (
|
||||
<View className="home">
|
||||
<Logo />
|
||||
<Text className="title">Welcome to Your Rax App!!!</Text>
|
||||
<Text className="info">More information about Rax</Text>
|
||||
<Text className="info" onClick={() => history.push('/about')}>Go About</Text>
|
||||
</View>
|
||||
);
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"module": "esNext",
|
||||
"target": "es2015",
|
||||
"outDir": "build",
|
||||
"jsx": "preserve",
|
||||
"jsxFactory": "createElement",
|
||||
"moduleResolution": "node",
|
||||
"sourceMap": true,
|
||||
"alwaysStrict": true,
|
||||
"baseUrl": "."
|
||||
},
|
||||
"include": ["src/*", ".rax"],
|
||||
"exclude": ["build"]
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
# with react miniapp
|
||||
|
||||
https://github.com/ice-lab/icejs/tree/master/examples
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"targets": ["miniapp", "wechat-miniprogram"]
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"name": "example-with-miniapp",
|
||||
"description": "miniapp example",
|
||||
"dependencies": {
|
||||
"react": "^16.4.1",
|
||||
"react-dom": "^16.4.1",
|
||||
"universal-request": "^2.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"miniapp-history": "^0.1.2",
|
||||
"miniapp-render": "1.2.0-1"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "icejs start",
|
||||
"build": "icejs build"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8.0.0"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
{
|
||||
"routes": [
|
||||
{
|
||||
"path": "/home",
|
||||
"source": "pages/Home/index"
|
||||
},
|
||||
{
|
||||
"path": "/about",
|
||||
"source": "pages/About/index"
|
||||
}
|
||||
],
|
||||
"window": {
|
||||
"title": "Mini App"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
import { runApp } from 'ice';
|
||||
|
||||
runApp({
|
||||
app: {
|
||||
onShow() {
|
||||
console.log('app show...');
|
||||
},
|
||||
onHide() {
|
||||
console.log('app hide...');
|
||||
}
|
||||
}
|
||||
});
|
|
@ -0,0 +1,30 @@
|
|||
import React, { useEffect } from 'react';
|
||||
import request from 'universal-request';
|
||||
|
||||
function getRepo(){
|
||||
request({
|
||||
url: 'https://ice.alicdn.com/assets/materials/react-materials.json',
|
||||
|
||||
}).then(res => {
|
||||
console.log('request res:', res);
|
||||
}).catch(err => {
|
||||
console.log(err);
|
||||
});
|
||||
}
|
||||
|
||||
const About = (props) => {
|
||||
|
||||
useEffect(() => {
|
||||
getRepo();
|
||||
});
|
||||
|
||||
const { history } = props;
|
||||
return (
|
||||
<div>
|
||||
<h2>About Page</h2>
|
||||
<p onClick={() => history.push('/home')}>go home</p>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default About;
|
|
@ -0,0 +1,19 @@
|
|||
.container {
|
||||
min-height: 600px;
|
||||
overflow: hidden;
|
||||
text-align: center;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.title {
|
||||
text-align: center;
|
||||
color: red;
|
||||
}
|
||||
|
||||
.description {
|
||||
margin-top: 40px;
|
||||
}
|
||||
|
||||
.action {
|
||||
margin-top: 40px;
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
import * as React from 'react';
|
||||
import { usePageShow, usePageHide } from 'ice';
|
||||
import styles from './index.module.scss';
|
||||
|
||||
const Home = (props) => {
|
||||
usePageShow(() => {
|
||||
console.log('page show...');
|
||||
});
|
||||
|
||||
usePageHide(() => {
|
||||
console.log('page hide...');
|
||||
});
|
||||
|
||||
const { history } = props;
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<h2 className={styles.title}>Welcome to icejs miniapp!</h2>
|
||||
<view className={styles.description}>This is a awesome project, enjoy it!</view>
|
||||
<view onClick={() => {
|
||||
console.log('Click');
|
||||
history.push('/about');
|
||||
}}>go about</view>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Home;
|
Binary file not shown.
After Width: | Height: | Size: 1.4 KiB |
Binary file not shown.
After Width: | Height: | Size: 1.1 KiB |
|
@ -0,0 +1,33 @@
|
|||
{
|
||||
"compileOnSave": false,
|
||||
"buildOnSave": false,
|
||||
"compilerOptions": {
|
||||
"baseUrl": ".",
|
||||
"outDir": "build",
|
||||
"module": "esnext",
|
||||
"target": "es6",
|
||||
"jsx": "react",
|
||||
"moduleResolution": "node",
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"lib": ["es6", "dom"],
|
||||
"sourceMap": true,
|
||||
"allowJs": true,
|
||||
"rootDir": "./",
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"noImplicitReturns": true,
|
||||
"noImplicitThis": true,
|
||||
"noImplicitAny": false,
|
||||
"importHelpers": true,
|
||||
"strictNullChecks": true,
|
||||
"suppressImplicitAnyIndexErrors": true,
|
||||
"noUnusedLocals": true,
|
||||
"skipLibCheck": true,
|
||||
"paths": {
|
||||
"@/*": ["./src/*"],
|
||||
"ice": [".ice/index.ts"],
|
||||
"ice/*": [".ice/pages/*"]
|
||||
}
|
||||
},
|
||||
"include": ["src/*", ".ice"],
|
||||
"exclude": ["node_modules", "build", "public"]
|
||||
}
|
|
@ -11,7 +11,7 @@ module.exports = {
|
|||
'testPathIgnorePatterns': [
|
||||
'/node_modules/',
|
||||
'/lib/',
|
||||
'icejs/bin/'
|
||||
'create-cli-utils/'
|
||||
],
|
||||
'preset': 'ts-jest'
|
||||
};
|
||||
|
|
|
@ -57,6 +57,7 @@
|
|||
"npm-run-all": "^4.1.5",
|
||||
"nsfw": "1.2.6",
|
||||
"pify": "^4.0.1",
|
||||
"rax": "^1.1.4",
|
||||
"react-test-renderer": "^16.13.1",
|
||||
"rimraf": "^3.0.0",
|
||||
"simple-git": "^1.132.0",
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
# `create-app-shared`
|
|
@ -0,0 +1,36 @@
|
|||
{
|
||||
"name": "create-app-shared",
|
||||
"version": "0.1.5",
|
||||
"description": "",
|
||||
"author": "ice-admin@alibaba-inc.com",
|
||||
"homepage": "https://github.com/alibaba/ice#readme",
|
||||
"license": "MIT",
|
||||
"main": "lib/index.js",
|
||||
"dependencies": {
|
||||
"deepmerge": "^4.2.2",
|
||||
"history": "^4.9.0",
|
||||
"miniapp-history": "^0.1.0",
|
||||
"query-string": "^6.13.1",
|
||||
"universal-env": "^3.0.0"
|
||||
},
|
||||
"directories": {
|
||||
"lib": "lib",
|
||||
"test": "__tests__"
|
||||
},
|
||||
"files": [
|
||||
"lib"
|
||||
],
|
||||
"publishConfig": {
|
||||
"registry": "http://registry.npmjs.com/"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/alibaba/ice.git"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "echo \"Error: run tests from root\" && exit 1"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/alibaba/ice/issues"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,118 @@
|
|||
import { isWeex } from 'universal-env';
|
||||
import { isMiniAppPlatform } from './env';
|
||||
import { SHOW, HIDE, ERROR, LAUNCH, NOT_FOUND, SHARE, TAB_ITEM_CLICK } from './constants';
|
||||
import { isFunction } from './utils';
|
||||
import { getHistory } from './history';
|
||||
import router from './router';
|
||||
import { emit as pageEmit } from './pageLifeCycles';
|
||||
|
||||
export const appCycles = {};
|
||||
|
||||
// eslint-disable-next-line
|
||||
declare var __weex_require__: any;
|
||||
|
||||
/**
|
||||
* Emit life cycle callback
|
||||
* @param {string} cycle cycle name
|
||||
* @param {object} context callback's context when executed
|
||||
* @param {...any} args callback params
|
||||
*/
|
||||
export function emit(cycle: any, context?: any, ...args) {
|
||||
if (Object.prototype.hasOwnProperty.call(appCycles, cycle)) {
|
||||
const cycles = appCycles[cycle];
|
||||
if (cycle === SHARE) {
|
||||
// In MiniApp, it need return callback result as share info, like { title, desc, path }
|
||||
args[0].content = context ? cycles[0].call(context, args[1]) : cycles[0](args[1]);
|
||||
} else {
|
||||
cycles.forEach(fn => {
|
||||
// eslint-disable-next-line
|
||||
context ? fn.apply(context, args) : fn(...args);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add app lifecycle callback
|
||||
* @param {string} cycle cycle name
|
||||
* @param {function} callback cycle callback
|
||||
*/
|
||||
export function addAppLifeCycle(cycle, callback) {
|
||||
if (isFunction(callback)) {
|
||||
// eslint-disable-next-line
|
||||
const cycles = appCycles[cycle] = appCycles[cycle] || [];
|
||||
cycles.push(callback);
|
||||
}
|
||||
}
|
||||
|
||||
// Emit MiniApp App lifeCycles
|
||||
if (isMiniAppPlatform) {
|
||||
window.addEventListener(LAUNCH, ({ options, context }: any) => {
|
||||
emit(LAUNCH, context, options);
|
||||
});
|
||||
window.addEventListener('appshow', ({ options, context }: any) => {
|
||||
emit(SHOW, context, options);
|
||||
});
|
||||
window.addEventListener('apphide', ({ context }: any) => {
|
||||
emit(HIDE, context);
|
||||
});
|
||||
window.addEventListener('apperror', ({ context, error }: any) => {
|
||||
emit(ERROR, context, error);
|
||||
});
|
||||
window.addEventListener('pagenotfound', ({ context }: any) => {
|
||||
emit(NOT_FOUND, context);
|
||||
});
|
||||
window.addEventListener('appshare', ({ context, shareInfo, options }: any) => {
|
||||
emit(SHARE, context, shareInfo, options);
|
||||
});
|
||||
window.addEventListener('tabitemclick', ({ options, context }: any) => {
|
||||
emit(TAB_ITEM_CLICK, context, options);
|
||||
});
|
||||
} else if (isWeex) {
|
||||
try {
|
||||
// https://weex.apache.org/docs/modules/globalEvent.html#addeventlistener
|
||||
// Use __weex_require__ in Rax project.
|
||||
const globalEvent = __weex_require__('@weex-module/globalEvent');
|
||||
globalEvent.addEventListener('WXApplicationDidBecomeActiveEvent', function() {
|
||||
router.current.visibiltyState = true;
|
||||
// Emit app show
|
||||
emit(SHOW);
|
||||
// Emit page show
|
||||
pageEmit(SHOW, router.current.pathname);
|
||||
});
|
||||
globalEvent.addEventListener('WXApplicationWillResignActiveEvent', function() {
|
||||
router.current.visibiltyState = false;
|
||||
// Emit page hide
|
||||
pageEmit(HIDE, router.current.pathname);
|
||||
// Emit app hide
|
||||
emit(HIDE);
|
||||
});
|
||||
} catch (err) {
|
||||
console.log(`@weex-module/globalEvent error: ${ err}`);
|
||||
}
|
||||
} else if (typeof document !== 'undefined' && typeof window !== 'undefined') {
|
||||
document.addEventListener('visibilitychange', function() {
|
||||
// Get history
|
||||
const history = getHistory();
|
||||
const currentPathName = history ? history.location.pathname : router.current.pathname;
|
||||
// The app switches from foreground to background
|
||||
if (currentPathName === router.current.pathname) {
|
||||
router.current.visibiltyState = !router.current.visibiltyState;
|
||||
if (router.current.visibiltyState) {
|
||||
// Emit app show
|
||||
emit(SHOW);
|
||||
// Emit page show
|
||||
pageEmit(SHOW, router.current.pathname);
|
||||
} else {
|
||||
// Emit page hide
|
||||
pageEmit(HIDE, router.current.pathname);
|
||||
// Emit app hide
|
||||
emit(HIDE);
|
||||
}
|
||||
}
|
||||
});
|
||||
// Emit error lifeCycles
|
||||
window.addEventListener('error', event => {
|
||||
emit(ERROR, null, event.error);
|
||||
});
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
import { addAppLifeCycle } from './appLifeCycles';
|
||||
import { SHOW, LAUNCH, ERROR, HIDE, TAB_ITEM_CLICK, NOT_FOUND, SHARE, UNHANDLED_REJECTION } from './constants';
|
||||
import { isMiniAppPlatform, isWeChatMiniProgram, isByteDanceMicroApp } from './env';
|
||||
|
||||
export default function collectAppLifeCycle(appConfig) {
|
||||
const { onLaunch, onShow, onError, onHide, onTabItemClick } = appConfig.app;
|
||||
// multi-end valid lifecycle
|
||||
// Add app lanuch callback
|
||||
addAppLifeCycle(LAUNCH, onLaunch);
|
||||
// Add app show callback
|
||||
addAppLifeCycle(SHOW, onShow);
|
||||
// Add app error callback
|
||||
addAppLifeCycle(ERROR, onError);
|
||||
// Add app hide callback
|
||||
addAppLifeCycle(HIDE, onHide);
|
||||
// Add tab bar item click callback
|
||||
addAppLifeCycle(TAB_ITEM_CLICK, onTabItemClick);
|
||||
// Add lifecycle callbacks which only valid in Wechat MiniProgram and ByteDance MicroApp
|
||||
if (isWeChatMiniProgram || isByteDanceMicroApp) {
|
||||
const { onPageNotFound, onShareAppMessage } = appConfig;
|
||||
// Add global share callback
|
||||
addAppLifeCycle(SHARE, onShareAppMessage);
|
||||
// Add page not found callback
|
||||
addAppLifeCycle(NOT_FOUND, onPageNotFound);
|
||||
}
|
||||
// Add lifecycle callbacks which only valid in Alibaba MiniApp
|
||||
if (isMiniAppPlatform) {
|
||||
const { onShareAppMessage, onUnhandledRejection } = appConfig;
|
||||
// Add global share callback
|
||||
addAppLifeCycle(SHARE, onShareAppMessage);
|
||||
// Add unhandledrejection callback
|
||||
addAppLifeCycle(UNHANDLED_REJECTION, onUnhandledRejection);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
export const SHOW = 'show';
|
||||
export const HIDE = 'hide';
|
||||
export const LAUNCH = 'launch';
|
||||
export const ERROR = 'error';
|
||||
export const NOT_FOUND = 'notfound';
|
||||
export const SHARE = 'share';
|
||||
export const TAB_ITEM_CLICK = 'tabitemclick';
|
||||
export const UNHANDLED_REJECTION = 'unhandledrejection';
|
|
@ -0,0 +1,52 @@
|
|||
import RuntimeModule from './runtimeModule';
|
||||
import { createHistory } from './history';
|
||||
import { isMiniAppPlatform } from './env';
|
||||
import collectAppLifeCycle from './collectAppLifeCycle';
|
||||
|
||||
// eslint-disable-next-line
|
||||
const deepmerge = require('deepmerge');
|
||||
|
||||
const DEFAULE_APP_CONFIG = {
|
||||
app: {
|
||||
rootId: 'root'
|
||||
},
|
||||
router: {
|
||||
type: 'hash'
|
||||
}
|
||||
};
|
||||
|
||||
export default ({ loadRuntimeModules, loadStaticModules, createElement }) => {
|
||||
const createBaseApp = (appConfig, buildConfig, context: any = {}) => {
|
||||
|
||||
appConfig = deepmerge(DEFAULE_APP_CONFIG, appConfig);
|
||||
|
||||
// load module to run before createApp ready
|
||||
loadStaticModules(appConfig);
|
||||
|
||||
// Set history
|
||||
let history = {};
|
||||
if (!isMiniAppPlatform) {
|
||||
const { router } = appConfig;
|
||||
const { type, basename } = router;
|
||||
history = createHistory({ type, basename });
|
||||
appConfig.router.history = history;
|
||||
}
|
||||
|
||||
context.createElement = createElement;
|
||||
|
||||
// Load runtime modules
|
||||
const runtime = new RuntimeModule(appConfig, buildConfig, context);
|
||||
loadRuntimeModules(runtime);
|
||||
|
||||
// Collect app lifeCyle
|
||||
collectAppLifeCycle(appConfig);
|
||||
|
||||
return {
|
||||
history,
|
||||
runtime,
|
||||
appConfig
|
||||
};
|
||||
};
|
||||
|
||||
return createBaseApp;
|
||||
};
|
|
@ -0,0 +1,41 @@
|
|||
import { getHistory } from './history';
|
||||
import router from './router';
|
||||
import { LAUNCH, SHOW, HIDE } from './constants';
|
||||
import { emit as appEmit } from './appLifeCycles';
|
||||
import { emit as pageEmit } from './pageLifeCycles';
|
||||
import { isMiniAppPlatform } from './env';
|
||||
|
||||
function emitLifeCycles() {
|
||||
// Get history
|
||||
const history = getHistory();
|
||||
const pathname = history.location.pathname;
|
||||
|
||||
// Set current router
|
||||
router.current = {
|
||||
pathname,
|
||||
visibiltyState: true
|
||||
};
|
||||
|
||||
if (!isMiniAppPlatform) {
|
||||
// Emit app lifecycle
|
||||
appEmit(LAUNCH);
|
||||
appEmit(SHOW);
|
||||
|
||||
// Listen history change
|
||||
history.listen((location) => {
|
||||
if (location.pathname !== router.current.pathname) {
|
||||
// Flow router info
|
||||
router.prev = router.current;
|
||||
router.current = {
|
||||
pathname: location.pathname,
|
||||
visibiltyState: true
|
||||
};
|
||||
router.prev.visibiltyState = false;
|
||||
pageEmit(HIDE, router.prev.pathname);
|
||||
pageEmit(SHOW, router.current.pathname);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export default emitLifeCycles;
|
|
@ -0,0 +1,27 @@
|
|||
import { isMiniAppPlatform } from './env';
|
||||
|
||||
function enhanceWithRouter({ withRouter, createElement }) {
|
||||
if (isMiniAppPlatform) {
|
||||
withRouter = function (Component) {
|
||||
function Wrapper(props) {
|
||||
// eslint-disable-next-line
|
||||
const history = window.history;
|
||||
return createElement(
|
||||
Component,
|
||||
Object.assign({}, props, {
|
||||
history,
|
||||
location: (history as any).location,
|
||||
})
|
||||
);
|
||||
}
|
||||
// eslint-disable-next-line
|
||||
Wrapper.displayName = 'withRouter(' + (Component.displayName || Component.name) + ')';
|
||||
Wrapper.WrappedComponent = Component;
|
||||
return Wrapper;
|
||||
};
|
||||
}
|
||||
|
||||
return withRouter;
|
||||
}
|
||||
|
||||
export default enhanceWithRouter;
|
|
@ -0,0 +1,4 @@
|
|||
import { isMiniApp, isWeChatMiniProgram, isByteDanceMicroApp } from 'universal-env';
|
||||
|
||||
export const isMiniAppPlatform = isMiniApp || isWeChatMiniProgram || isByteDanceMicroApp;
|
||||
export * from 'universal-env';
|
|
@ -0,0 +1,32 @@
|
|||
import {
|
||||
createBrowserHistory,
|
||||
createHashHistory,
|
||||
createMemoryHistory
|
||||
} from 'history';
|
||||
import { createMiniAppHistory } from 'miniapp-history';
|
||||
import { isMiniAppPlatform } from './env';
|
||||
|
||||
let history;
|
||||
|
||||
function createHistory({ routes, customHistory, type, basename }: any) {
|
||||
if (customHistory) {
|
||||
history = customHistory;
|
||||
} else if (type === 'hash') {
|
||||
history = createHashHistory({ basename });
|
||||
} else if (type === 'browser') {
|
||||
history = createBrowserHistory({ basename });
|
||||
} else if (isMiniAppPlatform) {
|
||||
(window as any).history = createMiniAppHistory(routes);
|
||||
window.location = (window.history as any).location;
|
||||
history = window.history;
|
||||
} else {
|
||||
history = createMemoryHistory();
|
||||
}
|
||||
return history;
|
||||
}
|
||||
|
||||
function getHistory() {
|
||||
return isMiniAppPlatform ? window.history : history;
|
||||
}
|
||||
|
||||
export { getHistory, createHistory };
|
|
@ -0,0 +1,43 @@
|
|||
import enhanceWithRouter from './enhanceWithRouter';
|
||||
import { withPageLifeCycle, createUsePageLifeCycle } from './pageLifeCycles';
|
||||
import emitLifeCycles from './emitLifeCycles';
|
||||
import createBaseApp from './createBaseApp';
|
||||
import { createHistory, getHistory } from './history';
|
||||
import { pathRedirect } from './utils';
|
||||
import {
|
||||
registerNativeEventListeners,
|
||||
addNativeEventListener,
|
||||
removeNativeEventListener
|
||||
} from './nativeEventListener';
|
||||
import useSearchParams from './useSearchParams';
|
||||
import withSearchParams from './withSearchParams';
|
||||
import collectAppLifeCycle from './collectAppLifeCycle';
|
||||
|
||||
function createShareAPI({ withRouter, createElement, useEffect }, loadRuntimeModules, loadStaticModules) {
|
||||
const { usePageShow, usePageHide } = createUsePageLifeCycle({ useEffect });
|
||||
return {
|
||||
createBaseApp: createBaseApp({ loadRuntimeModules, loadStaticModules, createElement }),
|
||||
|
||||
// history api
|
||||
withRouter: enhanceWithRouter({ withRouter, createElement }),
|
||||
createHistory,
|
||||
getHistory,
|
||||
useSearchParams,
|
||||
withSearchParams: withSearchParams(createElement),
|
||||
|
||||
// lifeCycle api
|
||||
emitLifeCycles,
|
||||
collectAppLifeCycle,
|
||||
usePageShow,
|
||||
usePageHide,
|
||||
withPageLifeCycle,
|
||||
|
||||
// utils api
|
||||
pathRedirect,
|
||||
registerNativeEventListeners,
|
||||
addNativeEventListener,
|
||||
removeNativeEventListener
|
||||
};
|
||||
};
|
||||
|
||||
export default createShareAPI;
|
|
@ -0,0 +1,12 @@
|
|||
// eslint-disable-next-line
|
||||
export function registerNativeEventListeners(Klass, events) {
|
||||
// For rax miniapp runtime babel plugins prev compile
|
||||
}
|
||||
|
||||
export function addNativeEventListener(eventName, callback) {
|
||||
window.addEventListener(eventName, callback);
|
||||
}
|
||||
|
||||
export function removeNativeEventListener(evetName, callback) {
|
||||
window.removeEventListener(evetName, callback);
|
||||
}
|
|
@ -0,0 +1,108 @@
|
|||
import { getHistory } from './history';
|
||||
import { isMiniAppPlatform } from './env';
|
||||
import { SHOW, HIDE } from './constants';
|
||||
import router from './router';
|
||||
|
||||
// visibleListeners => { [pathname]: { show: [], hide: [] } }
|
||||
const visibleListeners = {};
|
||||
|
||||
function addPageLifeCycle(cycle, callback) {
|
||||
const pathname = router.current.pathname;
|
||||
|
||||
if (!visibleListeners[pathname]) {
|
||||
visibleListeners[pathname] = {
|
||||
[SHOW]: [],
|
||||
[HIDE]: []
|
||||
};
|
||||
}
|
||||
visibleListeners[pathname][cycle].push(callback);
|
||||
}
|
||||
|
||||
export function emit(cycle: any, pathname?: string, ...args) {
|
||||
// Ensure queue exists
|
||||
if (visibleListeners[pathname] && visibleListeners[pathname][cycle]) {
|
||||
for (let i = 0, l = visibleListeners[pathname][cycle].length; i < l; i++) {
|
||||
visibleListeners[pathname][cycle][i](...args);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function createPageLifeCycle(useEffect) {
|
||||
return (cycle, callback) => {
|
||||
useEffect(() => {
|
||||
// When component did mount, it will trigger usePageShow callback
|
||||
if (cycle === SHOW) {
|
||||
callback();
|
||||
}
|
||||
const pathname = router.current.pathname;
|
||||
|
||||
addPageLifeCycle(cycle, callback);
|
||||
|
||||
return () => {
|
||||
if (visibleListeners[pathname]) {
|
||||
const index = visibleListeners[pathname][cycle].indexOf(callback);
|
||||
if (index > -1) {
|
||||
visibleListeners[pathname][cycle].splice(index, 1);
|
||||
}
|
||||
}
|
||||
};
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
};
|
||||
}
|
||||
|
||||
export function withPageLifeCycle(Component) {
|
||||
class Wrapper extends Component {
|
||||
constructor() {
|
||||
super();
|
||||
if (this.onShow) {
|
||||
if (!isMiniAppPlatform) {
|
||||
// In MiniApp platform show event will trigger after addPageLifeCycle, so it needn't be execute in constructor
|
||||
this.onShow();
|
||||
}
|
||||
addPageLifeCycle(SHOW, this.onShow.bind(this));
|
||||
}
|
||||
if (this.onHide) {
|
||||
addPageLifeCycle(HIDE, this.onHide.bind(this));
|
||||
}
|
||||
// Keep the path name corresponding to current page component
|
||||
this.pathname = router.current.pathname;
|
||||
}
|
||||
|
||||
private componentWillUnmount() {
|
||||
visibleListeners[this.pathname] = null;
|
||||
}
|
||||
}
|
||||
Wrapper.displayName = `withPageLifeCycle(${ Component.displayName || Component.name })`;
|
||||
return Wrapper as any;
|
||||
}
|
||||
|
||||
if (isMiniAppPlatform) {
|
||||
// eslint-disable-next-line
|
||||
window.addEventListener('pageshow', () => {
|
||||
// Get history
|
||||
const history = getHistory();
|
||||
emit(SHOW, history.location.pathname);
|
||||
});
|
||||
// eslint-disable-next-line
|
||||
window.addEventListener('pagehide', () => {
|
||||
// Get history
|
||||
const history = getHistory();
|
||||
emit(HIDE, history.location.pathname);
|
||||
});
|
||||
}
|
||||
|
||||
export function createUsePageLifeCycle({ useEffect }) {
|
||||
const usePageShow = (callback) => {
|
||||
createPageLifeCycle(useEffect)(SHOW, callback);
|
||||
};
|
||||
|
||||
const usePageHide = (callback) => {
|
||||
createPageLifeCycle(useEffect)(HIDE, callback);
|
||||
};
|
||||
|
||||
return {
|
||||
usePageShow,
|
||||
usePageHide
|
||||
};
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
import { isMiniAppPlatform } from './env';
|
||||
import { getHistory } from './history';
|
||||
|
||||
const current = {
|
||||
pathname: '/',
|
||||
visibiltyState: true
|
||||
};
|
||||
|
||||
const router = {
|
||||
prev: null,
|
||||
current
|
||||
};
|
||||
|
||||
Object.defineProperty(router, 'current', {
|
||||
get() {
|
||||
if (!isMiniAppPlatform) {
|
||||
return current;
|
||||
}
|
||||
const history = getHistory();
|
||||
return Object.assign(current, {
|
||||
pathname: history.location.pathname
|
||||
});
|
||||
},
|
||||
set(value) {
|
||||
Object.assign(current, value);
|
||||
}
|
||||
});
|
||||
|
||||
export default router;
|
|
@ -1,66 +1,25 @@
|
|||
import * as React from 'react';
|
||||
import { createHistory } from './history';
|
||||
|
||||
interface IRenderParams {
|
||||
App: React.ComponentType;
|
||||
appMountNode: HTMLElement;
|
||||
}
|
||||
|
||||
interface IDOMRender {
|
||||
(data: IRenderParams): void;
|
||||
}
|
||||
|
||||
interface IGetAppRouter {
|
||||
(): React.ComponentType;
|
||||
}
|
||||
|
||||
interface RouteItemProps {
|
||||
children?: RouteItemProps[];
|
||||
path?: string;
|
||||
redirect?: string;
|
||||
component?: React.ComponentType;
|
||||
RouteWrapper?: React.ComponentType;
|
||||
}
|
||||
|
||||
interface IModifyRoutes {
|
||||
(modifyFn: IModifyFn): void;
|
||||
}
|
||||
|
||||
interface IModifyFn {
|
||||
(routes: RouteItemProps[]): RouteItemProps[];
|
||||
}
|
||||
|
||||
interface IAppRouter {
|
||||
(routes: RouteItemProps[]): React.ComponentType;
|
||||
}
|
||||
|
||||
interface IWrapperRoute {
|
||||
(RouteComponent: React.ComponentType): React.ComponentType;
|
||||
}
|
||||
|
||||
interface IWrapperRouteComponent {
|
||||
(wrapperRoute: IWrapperRoute): void;
|
||||
}
|
||||
|
||||
class RuntimeModule {
|
||||
private AppProvider: React.ComponentType[];
|
||||
|
||||
private modifyRoutesRegistration: IModifyFn[];
|
||||
private renderRouter: any;
|
||||
|
||||
private wrapperRouteRegistration: IWrapperRoute[];
|
||||
private AppProvider: any;
|
||||
|
||||
public renderRouter: IAppRouter;
|
||||
private appConfig: any;
|
||||
|
||||
public appConfig: any;
|
||||
private buildConfig: any;
|
||||
|
||||
public context: any;
|
||||
private context: any;
|
||||
|
||||
public buildConfig: any;
|
||||
private modifyDOMRender: any;
|
||||
|
||||
public modifyDOMRender: IDOMRender|null;
|
||||
private modifyRoutesRegistration: any;
|
||||
|
||||
private wrapperRouteRegistration: any;
|
||||
|
||||
constructor(appConfig, buildConfig, context) {
|
||||
this.renderRouter = () => () => <div>No route</div>;
|
||||
this.renderRouter = () => () => context.createElement('div', null, 'No route');
|
||||
this.AppProvider = [];
|
||||
this.appConfig = appConfig;
|
||||
this.buildConfig = buildConfig;
|
||||
|
@ -78,7 +37,7 @@ class RuntimeModule {
|
|||
modifyRoutes: this.modifyRoutes,
|
||||
wrapperRouteComponent: this.wrapperRouteComponent,
|
||||
createHistory
|
||||
}
|
||||
};
|
||||
|
||||
if (module) module.default({
|
||||
...runtimeAPI,
|
||||
|
@ -100,28 +59,31 @@ class RuntimeModule {
|
|||
if (!this.AppProvider.length) return null;
|
||||
return this.AppProvider.reduce((ProviderComponent, CurrentProvider) => {
|
||||
return ({ children, ...rest }) => {
|
||||
return (
|
||||
<ProviderComponent {...rest}>
|
||||
{CurrentProvider ? <CurrentProvider {...rest}>{children}</CurrentProvider> : children}
|
||||
</ProviderComponent>
|
||||
)
|
||||
}
|
||||
const element = CurrentProvider
|
||||
? this.context.createElement(CurrentProvider, {...rest})
|
||||
: this.context.createElement(children);
|
||||
return this.context.createElement(
|
||||
ProviderComponent,
|
||||
{...rest},
|
||||
element
|
||||
);
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
public addDOMRender = (render: IDOMRender) => {
|
||||
public addDOMRender = (render) => {
|
||||
this.modifyDOMRender = render;
|
||||
}
|
||||
|
||||
public modifyRoutes: IModifyRoutes = (modifyFn) => {
|
||||
public modifyRoutes = (modifyFn) => {
|
||||
this.modifyRoutesRegistration.push(modifyFn);
|
||||
}
|
||||
|
||||
public wrapperRouteComponent: IWrapperRouteComponent = (wrapperRoute) => {
|
||||
public wrapperRouteComponent = (wrapperRoute) => {
|
||||
this.wrapperRouteRegistration.push(wrapperRoute);
|
||||
}
|
||||
|
||||
private wrapperRoutes = (routes) => {
|
||||
public wrapperRoutes = (routes) => {
|
||||
return routes.map((item) => {
|
||||
if (item.children) {
|
||||
item.children = this.wrapperRoutes(item.children);
|
||||
|
@ -132,7 +94,7 @@ class RuntimeModule {
|
|||
});
|
||||
}
|
||||
|
||||
public getAppRouter: IGetAppRouter = () => {
|
||||
public getAppRouter = () => {
|
||||
const routes = this.wrapperRoutes(this.modifyRoutesRegistration.reduce((acc, curr) => {
|
||||
return curr(acc);
|
||||
}, []));
|
|
@ -0,0 +1,9 @@
|
|||
import * as queryString from 'query-string';
|
||||
import { getHistory } from './history';
|
||||
|
||||
const useSearchParams = () => {
|
||||
const history = getHistory();
|
||||
return queryString.parse(history.location.search);
|
||||
};
|
||||
|
||||
export default useSearchParams;
|
|
@ -0,0 +1,7 @@
|
|||
import { isFunction } from './type';
|
||||
import pathRedirect from './pathRedirect';
|
||||
|
||||
export {
|
||||
isFunction,
|
||||
pathRedirect
|
||||
};
|
|
@ -0,0 +1,55 @@
|
|||
/* eslint no-undef:0 */
|
||||
// In a Single-Page Application, sometimes we need to jump to a specific route.
|
||||
// It is very simple in the Web application, url like #/xxx can jump to the corresponding page.
|
||||
|
||||
// Things seem to be very complicated in Weex, because using `MemoryHistory`,
|
||||
// which is used as a reference implementation and may also be used in non-DOM environments.
|
||||
// We cannot jump directly through url.
|
||||
|
||||
import { isWeex, isWeb } from 'universal-env';
|
||||
|
||||
// We want to control applications on different platforms to jump to specific pages through unified parameters.
|
||||
// Like https://xxx.com?_path=/page1, use `_path` to jump to the specific route.
|
||||
const TARGET_PATH_REG = /[?&]_path=([^&#]+)/i;
|
||||
|
||||
export default function pathRedirect(history, routes) {
|
||||
let targetPath = '';
|
||||
let targetQuery = null;
|
||||
|
||||
// In Web, use location.search first
|
||||
if (isWeb && TARGET_PATH_REG.test(window.location.search)) {
|
||||
targetQuery = window.location.search.match(TARGET_PATH_REG);
|
||||
}
|
||||
|
||||
// In Weex, use location.href first. Support by rax-weex framework
|
||||
if (isWeex && TARGET_PATH_REG.test(window.location.href)) {
|
||||
targetQuery = window.location.href.match(TARGET_PATH_REG);
|
||||
}
|
||||
|
||||
// If there is no `_path` in url search, try history.location.
|
||||
if (!targetQuery && TARGET_PATH_REG.test(history.location.search)) {
|
||||
targetQuery = history.location.search.match(TARGET_PATH_REG);
|
||||
}
|
||||
|
||||
let isConfirmed = false;
|
||||
targetPath = targetQuery ? targetQuery[1] : '';
|
||||
|
||||
for (let i = 0, l = routes.length; i < l; i++) {
|
||||
if (targetPath === routes[i].path) {
|
||||
isConfirmed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (targetPath && !isConfirmed) {
|
||||
console.warn(
|
||||
'Warning: url query `_path` should be an exist path in app.json, see: https://rax.js.org/docs/guide/routes '
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
// If `targetPath` exists, jump to the specific route.
|
||||
if (targetPath) {
|
||||
history.replace(targetPath + history.location.search);
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
export const isFunction = target => typeof target === 'function';
|
|
@ -0,0 +1,20 @@
|
|||
import * as queryString from 'query-string';
|
||||
import { getHistory } from './history';
|
||||
|
||||
export default (createElement) => {
|
||||
const withSearchParams = Component => {
|
||||
const WrapperedSearchParams = props => {
|
||||
const history = getHistory();
|
||||
const searchParams = queryString.parse(history.location.search);
|
||||
return createElement(
|
||||
Component,
|
||||
{
|
||||
...props,
|
||||
searchParams
|
||||
}
|
||||
);
|
||||
};
|
||||
return WrapperedSearchParams;
|
||||
};
|
||||
return withSearchParams;
|
||||
};
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"extends": "../../tsconfig.settings.json",
|
||||
"compilerOptions": {
|
||||
"baseUrl": "./",
|
||||
"rootDir": "src",
|
||||
"outDir": "lib"
|
||||
},
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
# `create-cli-utils`
|
|
@ -0,0 +1,37 @@
|
|||
{
|
||||
"name": "create-cli-utils",
|
||||
"version": "0.1.1",
|
||||
"description": "",
|
||||
"author": "ice-admin@alibaba-inc.com",
|
||||
"homepage": "https://github.com/alibaba/ice#readme",
|
||||
"license": "MIT",
|
||||
"main": "lib/index.js",
|
||||
"directories": {
|
||||
"lib": "lib",
|
||||
"test": "__tests__"
|
||||
},
|
||||
"files": [
|
||||
"lib"
|
||||
],
|
||||
"dependencies": {
|
||||
"@alib/build-scripts": "^0.1.24",
|
||||
"chokidar": "^3.3.1",
|
||||
"commander": "^5.0.0",
|
||||
"detect-port": "^1.3.0",
|
||||
"inquirer": "^7.1.0",
|
||||
"yargs-parser": "^18.1.2"
|
||||
},
|
||||
"publishConfig": {
|
||||
"registry": "http://registry.npmjs.com/"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/alibaba/ice.git"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "echo \"Error: run tests from root\" && exit 1"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/alibaba/ice/issues"
|
||||
}
|
||||
}
|
|
@ -2,9 +2,8 @@
|
|||
const parse = require('yargs-parser');
|
||||
const { build } = require('@alib/build-scripts');
|
||||
const log = require('@alib/build-scripts/lib/utils/log');
|
||||
const getBuiltInPlugins = require('../lib/index');
|
||||
|
||||
module.exports = async () => {
|
||||
module.exports = async (getBuiltInPlugins) => {
|
||||
process.env.NODE_ENV = 'production';
|
||||
const rawArgv = parse(process.argv.slice(2), {
|
||||
configuration: { 'strip-dashed': true },
|
|
@ -0,0 +1,55 @@
|
|||
#!/usr/bin/env node
|
||||
const detect = require('detect-port');
|
||||
const inquirer = require('inquirer');
|
||||
const parse = require('yargs-parser');
|
||||
const { start } = require('@alib/build-scripts');
|
||||
const log = require('@alib/build-scripts/lib/utils/log');
|
||||
|
||||
const rawArgv = parse(process.argv.slice(2), {
|
||||
configuration: { 'strip-dashed': true }
|
||||
});
|
||||
|
||||
const DEFAULT_PORT = rawArgv.port || process.env.PORT || 3333;
|
||||
const defaultPort = parseInt(DEFAULT_PORT, 10);
|
||||
|
||||
module.exports = async (getBuiltInPlugins) => {
|
||||
let newPort = await detect(defaultPort);
|
||||
if (newPort !== defaultPort) {
|
||||
const question = {
|
||||
type: 'confirm',
|
||||
name: 'shouldChangePort',
|
||||
message: `${defaultPort} 端口已被占用,是否使用 ${newPort} 端口启动?`,
|
||||
default: true
|
||||
};
|
||||
const answer = await inquirer.prompt(question);
|
||||
if (!answer.shouldChangePort) {
|
||||
newPort = null;
|
||||
}
|
||||
}
|
||||
if (newPort === null) {
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
process.env.NODE_ENV = 'development';
|
||||
rawArgv.port = parseInt(newPort, 10);
|
||||
|
||||
// ignore _ in rawArgv
|
||||
delete rawArgv._;
|
||||
try {
|
||||
const devServer = await start({
|
||||
args: { ...rawArgv },
|
||||
getBuiltInPlugins
|
||||
});
|
||||
|
||||
['SIGINT', 'SIGTERM'].forEach(function(sig) {
|
||||
process.on(sig, function() {
|
||||
devServer.close();
|
||||
process.exit(0);
|
||||
});
|
||||
});
|
||||
} catch (err) {
|
||||
log.error(err.message);
|
||||
console.error(err);
|
||||
process.exit(1);
|
||||
}
|
||||
};
|
|
@ -0,0 +1,61 @@
|
|||
#!/usr/bin/env node
|
||||
const program = require('commander');
|
||||
const checkNodeVersion = require('@alib/build-scripts/lib/utils/checkNodeVersion');
|
||||
const start = require('./start');
|
||||
const build = require('./build');
|
||||
const test = require('./test');
|
||||
|
||||
module.exports = async (getBuiltInPlugins, forkChildProcessPath, packageInfo) => {
|
||||
console.log(packageInfo.name, packageInfo.version);
|
||||
// finish check before run command
|
||||
checkNodeVersion(packageInfo.engines.node);
|
||||
|
||||
program
|
||||
.version(packageInfo.version)
|
||||
.usage('<command> [options]');
|
||||
|
||||
program
|
||||
.command('build')
|
||||
.description('build project')
|
||||
.allowUnknownOption()
|
||||
.option('--config <config>', 'use custom config')
|
||||
.action(async function() {
|
||||
await build(getBuiltInPlugins);
|
||||
});
|
||||
|
||||
program
|
||||
.command('start')
|
||||
.description('start server')
|
||||
.allowUnknownOption()
|
||||
.option('--config <config>', 'use custom config')
|
||||
.option('-h, --host <host>', 'dev server host', '0.0.0.0')
|
||||
.option('-p, --port <port>', 'dev server port')
|
||||
.action(async function() {
|
||||
await start(getBuiltInPlugins, forkChildProcessPath);
|
||||
});
|
||||
|
||||
program
|
||||
.command('test')
|
||||
.description('run tests with jest')
|
||||
.allowUnknownOption() // allow jest config
|
||||
.option('--config <config>', 'use custom config')
|
||||
.action(async function() {
|
||||
await test(getBuiltInPlugins);
|
||||
});
|
||||
|
||||
program.parse(process.argv);
|
||||
|
||||
const proc = program.runningCommand;
|
||||
|
||||
if (proc) {
|
||||
proc.on('close', process.exit.bind(process));
|
||||
proc.on('error', () => {
|
||||
process.exit(1);
|
||||
});
|
||||
}
|
||||
|
||||
const subCmd = program.args[0];
|
||||
if (!subCmd) {
|
||||
program.help();
|
||||
}
|
||||
};
|
|
@ -0,0 +1,11 @@
|
|||
const start = require('./start');
|
||||
const build = require('./build');
|
||||
const test = require('./test');
|
||||
const childProcessStart = require('./child-process-start');
|
||||
|
||||
module.exports = {
|
||||
start,
|
||||
build,
|
||||
test,
|
||||
childProcessStart
|
||||
};
|
|
@ -8,7 +8,6 @@ const log = require('@alib/build-scripts/lib/utils/log');
|
|||
|
||||
let child = null;
|
||||
const rawArgv = parse(process.argv.slice(2));
|
||||
const scriptPath = require.resolve('./child-process-start.js');
|
||||
const configPath = path.resolve(rawArgv.config || 'build.json');
|
||||
|
||||
const inspectRegExp = /^--(inspect(?:-brk)?)(?:=(?:([^:]+):)?(\d+))?$/;
|
||||
|
@ -29,11 +28,11 @@ function modifyInspectArgv(argv) {
|
|||
);
|
||||
}
|
||||
|
||||
function restartProcess() {
|
||||
function restartProcess(forkChildProcessPath) {
|
||||
(async () => {
|
||||
// remove the inspect related argv when passing to child process to avoid port-in-use error
|
||||
const argv = await modifyInspectArgv(process.execArgv);
|
||||
child = fork(scriptPath, process.argv.slice(2), { execArgv: argv });
|
||||
child = fork(forkChildProcessPath, process.argv.slice(2), { execArgv: argv });
|
||||
child.on('message', data => {
|
||||
if (process.send) {
|
||||
process.send(data);
|
||||
|
@ -48,25 +47,23 @@ function restartProcess() {
|
|||
})();
|
||||
}
|
||||
|
||||
const onUserChange = () => {
|
||||
module.exports = (getBuiltInPlugins, forkChildProcessPath) => {
|
||||
restartProcess(forkChildProcessPath);
|
||||
|
||||
const watcher = chokidar.watch(configPath, {
|
||||
ignoreInitial: true,
|
||||
});
|
||||
|
||||
watcher.on('change', function() {
|
||||
console.log('\n');
|
||||
log.info('build.json has been changed');
|
||||
log.info('restart dev server');
|
||||
// add process env for mark restart dev process
|
||||
process.env.RESTART_DEV = true;
|
||||
child.kill();
|
||||
restartProcess();
|
||||
};
|
||||
|
||||
module.exports = () => {
|
||||
restartProcess();
|
||||
|
||||
const watcher = chokidar.watch(configPath, {
|
||||
ignoreInitial: true,
|
||||
restartProcess(forkChildProcessPath);
|
||||
});
|
||||
|
||||
watcher.on('change', onUserChange);
|
||||
|
||||
watcher.on('error', error => {
|
||||
log.error('fail to watch file', error);
|
||||
process.exit(1);
|
|
@ -2,9 +2,8 @@
|
|||
const parse = require('yargs-parser');
|
||||
const { test } = require('@alib/build-scripts');
|
||||
const log = require('@alib/build-scripts/lib/utils/log');
|
||||
const getBuiltInPlugins = require('../lib/index');
|
||||
|
||||
module.exports = async () => {
|
||||
module.exports = async (getBuiltInPlugins) => {
|
||||
process.env.NODE_ENV = 'test';
|
||||
const rawArgv = parse(process.argv.slice(3), {
|
||||
configuration: { 'strip-dashed': true },
|
|
@ -22,7 +22,7 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"@iceworks/generate-project": "^1.0.0",
|
||||
"chalk": "^3.0.0",
|
||||
"chalk": "^4.0.0",
|
||||
"fs-extra": "^8.1.0",
|
||||
"inquirer": "^7.0.4"
|
||||
},
|
||||
|
|
|
@ -1,56 +1,7 @@
|
|||
#!/usr/bin/env node
|
||||
const detect = require('detect-port');
|
||||
const inquirer = require('inquirer');
|
||||
const parse = require('yargs-parser');
|
||||
const { start } = require('@alib/build-scripts');
|
||||
const log = require('@alib/build-scripts/lib/utils/log');
|
||||
const { childProcessStart } = require('create-cli-utils');
|
||||
const getBuiltInPlugins = require('../lib/index');
|
||||
|
||||
const rawArgv = parse(process.argv.slice(2), {
|
||||
configuration: { 'strip-dashed': true },
|
||||
});
|
||||
|
||||
const DEFAULT_PORT = rawArgv.port || process.env.PORT || 3333;
|
||||
const defaultPort = parseInt(DEFAULT_PORT, 10);
|
||||
|
||||
(async() => {
|
||||
let newPort = await detect(defaultPort);
|
||||
if (newPort !== defaultPort) {
|
||||
const question = {
|
||||
type: 'confirm',
|
||||
name: 'shouldChangePort',
|
||||
message: `${defaultPort} 端口已被占用,是否使用 ${newPort} 端口启动?`,
|
||||
default: true,
|
||||
};
|
||||
const answer = await inquirer.prompt(question);
|
||||
if (!answer.shouldChangePort) {
|
||||
newPort = null;
|
||||
}
|
||||
}
|
||||
if (newPort === null) {
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
process.env.NODE_ENV = 'development';
|
||||
rawArgv.port = parseInt(newPort, 10);
|
||||
|
||||
// ignore _ in rawArgv
|
||||
delete rawArgv._;
|
||||
try {
|
||||
const devServer = await start({
|
||||
args: { ...rawArgv },
|
||||
getBuiltInPlugins,
|
||||
});
|
||||
|
||||
['SIGINT', 'SIGTERM'].forEach(function(sig) {
|
||||
process.on(sig, function() {
|
||||
devServer.close();
|
||||
process.exit(0);
|
||||
});
|
||||
});
|
||||
} catch (err) {
|
||||
log.error(err.message);
|
||||
console.error(err);
|
||||
process.exit(1);
|
||||
}
|
||||
await childProcessStart(getBuiltInPlugins);
|
||||
})();
|
||||
|
|
|
@ -1,65 +1,10 @@
|
|||
#!/usr/bin/env node
|
||||
const program = require('commander');
|
||||
const checkNodeVersion = require('@alib/build-scripts/lib/utils/checkNodeVersion');
|
||||
const createCli = require('create-cli-utils/lib/cli');
|
||||
const packageInfo = require('../package.json');
|
||||
const build = require('./build');
|
||||
const start = require('./start');
|
||||
const test = require('./test');
|
||||
const getBuiltInPlugins = require('../lib');
|
||||
|
||||
const forkChildProcessPath = require.resolve('./child-process-start');
|
||||
|
||||
(async () => {
|
||||
console.log(packageInfo.name, packageInfo.version);
|
||||
// finish check before run command
|
||||
checkNodeVersion(packageInfo.engines.node);
|
||||
|
||||
program
|
||||
.version(packageInfo.version)
|
||||
.usage('<command> [options]');
|
||||
|
||||
program
|
||||
.command('build')
|
||||
.description('build project')
|
||||
.allowUnknownOption()
|
||||
.option('--config <config>', 'use custom config')
|
||||
.option('--analyzer', 'use webpack-bundle-analyzer')
|
||||
.option('--analyzer-port <port>', 'webpack-bundle-analyzer port', '9000')
|
||||
.action(build);
|
||||
|
||||
program
|
||||
.command('start')
|
||||
.description('start server')
|
||||
.allowUnknownOption()
|
||||
.option('--config <config>', 'use custom config')
|
||||
.option('-h, --host <host>', 'dev server host', '0.0.0.0')
|
||||
.option('-p, --port <port>', 'dev server port')
|
||||
.option('--https', 'use https protocol')
|
||||
.option('--analyzer', 'use webpack-bundle-analyzer')
|
||||
.option('--analyzer-port <port>', 'webpack-bundle-analyzer port', '9000')
|
||||
.option('--disable-reload', 'disable hot-loader module')
|
||||
.option('--disable-mock', 'diable mock service')
|
||||
.option('--disable-open', 'disable automatically open browser')
|
||||
.option('--disable-assets', 'disable the output of webpack assets')
|
||||
.action(start);
|
||||
|
||||
program
|
||||
.command('test')
|
||||
.description('run tests with jest')
|
||||
.allowUnknownOption() // allow jest config
|
||||
.option('--config <config>', 'use custom config')
|
||||
.action(test);
|
||||
|
||||
program.parse(process.argv);
|
||||
|
||||
const proc = program.runningCommand;
|
||||
|
||||
if (proc) {
|
||||
proc.on('close', process.exit.bind(process));
|
||||
proc.on('error', () => {
|
||||
process.exit(1);
|
||||
});
|
||||
}
|
||||
|
||||
const subCmd = program.args[0];
|
||||
if (!subCmd) {
|
||||
program.help();
|
||||
}
|
||||
await createCli(getBuiltInPlugins, forkChildProcessPath, packageInfo);
|
||||
})();
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "ice.js",
|
||||
"version": "1.6.4-alpha.0",
|
||||
"version": "1.7.0-10",
|
||||
"description": "command line interface and builtin plugin for icejs",
|
||||
"author": "ice-admin@alibaba-inc.com",
|
||||
"homepage": "",
|
||||
|
@ -25,16 +25,18 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"@alib/build-scripts": "^0.1.13",
|
||||
"build-plugin-ice-config": "1.6.4-alpha.0",
|
||||
"build-plugin-ice-core": "1.6.4-alpha.0",
|
||||
"build-plugin-ice-helpers": "1.6.4-alpha.0",
|
||||
"build-plugin-ice-logger": "1.6.4-alpha.0",
|
||||
"build-plugin-ice-mpa": "1.6.4-alpha.0",
|
||||
"build-plugin-ice-request": "1.6.4-alpha.0",
|
||||
"build-plugin-ice-router": "1.6.4-alpha.0",
|
||||
"build-plugin-ice-ssr": "1.6.4-alpha.0",
|
||||
"build-plugin-ice-store": "1.6.4-alpha.0",
|
||||
"build-plugin-react-app": "1.6.4-alpha.0",
|
||||
"build-plugin-ice-config": "1.7.0-0",
|
||||
"build-plugin-app-core": "0.1.6",
|
||||
"build-plugin-ice-helpers": "1.7.0-0",
|
||||
"build-plugin-ice-logger": "1.7.0-0",
|
||||
"build-plugin-ice-mpa": "1.7.0-0",
|
||||
"build-plugin-ice-request": "1.7.0-0",
|
||||
"build-plugin-ice-router": "1.7.0-0",
|
||||
"build-plugin-ice-ssr": "1.7.0-1",
|
||||
"build-plugin-ice-store": "1.7.0-0",
|
||||
"build-plugin-react-app": "1.7.0-4",
|
||||
"build-plugin-miniapp": "0.1.2",
|
||||
"create-cli-utils": "^0.1.0",
|
||||
"chokidar": "^3.3.1",
|
||||
"commander": "^5.0.0",
|
||||
"detect-port": "^1.3.0",
|
||||
|
|
|
@ -6,27 +6,53 @@ const getBuiltInPlugins = (userConfig) => {
|
|||
];
|
||||
}
|
||||
|
||||
// built-in plugins for icejs
|
||||
const builtInPlugins = [
|
||||
'build-plugin-ice-core',
|
||||
'build-plugin-react-app',
|
||||
let plugins = [];
|
||||
|
||||
// common plugins
|
||||
const commonPlugins = [
|
||||
[
|
||||
'build-plugin-app-core', {
|
||||
'framework': 'react'
|
||||
}
|
||||
],
|
||||
'build-plugin-react-app'
|
||||
];
|
||||
|
||||
// for ice/react plugins
|
||||
const reactAppPlugins = [
|
||||
'build-plugin-ice-router',
|
||||
'build-plugin-ice-helpers',
|
||||
'build-plugin-ice-logger',
|
||||
'build-plugin-ice-config',
|
||||
'build-plugin-ice-request',
|
||||
'build-plugin-ice-mpa'
|
||||
'build-plugin-ice-mpa',
|
||||
'build-plugin-ice-request'
|
||||
];
|
||||
|
||||
// for ice/miniapp plugins
|
||||
const miniAppPlugins = [
|
||||
'build-plugin-miniapp'
|
||||
];
|
||||
|
||||
// for miniapp plugins
|
||||
if (Array.isArray(userConfig.targets)
|
||||
&& (userConfig.targets.includes('miniapp') || userConfig.targets.includes('wechat-miniprogram'))
|
||||
) {
|
||||
// @ts-ignore
|
||||
plugins = commonPlugins.concat(miniAppPlugins);
|
||||
} else {
|
||||
plugins = commonPlugins.concat(reactAppPlugins);
|
||||
}
|
||||
|
||||
if (userConfig.ssr) {
|
||||
builtInPlugins.push('build-plugin-ice-ssr');
|
||||
plugins.push('build-plugin-ice-ssr');
|
||||
}
|
||||
|
||||
// add store plugin
|
||||
if (!Object.prototype.hasOwnProperty.call(userConfig, 'store') || userConfig.store !== false) {
|
||||
builtInPlugins.push('build-plugin-ice-store');
|
||||
plugins.push('build-plugin-ice-store');
|
||||
}
|
||||
|
||||
return builtInPlugins;
|
||||
return plugins;
|
||||
};
|
||||
|
||||
export = getBuiltInPlugins
|
||||
module.exports = getBuiltInPlugins;
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
# `miniapp-renderer`
|
|
@ -0,0 +1,32 @@
|
|||
{
|
||||
"name": "miniapp-renderer",
|
||||
"version": "0.1.3",
|
||||
"description": "",
|
||||
"author": "ice-admin@alibaba-inc.com",
|
||||
"homepage": "https://github.com/alibaba/ice#readme",
|
||||
"license": "MIT",
|
||||
"main": "lib/index.js",
|
||||
"dependencies": {
|
||||
"universal-env": "^3.0.0"
|
||||
},
|
||||
"directories": {
|
||||
"lib": "lib",
|
||||
"test": "__tests__"
|
||||
},
|
||||
"files": [
|
||||
"lib"
|
||||
],
|
||||
"publishConfig": {
|
||||
"registry": "http://registry.npmjs.com/"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/alibaba/ice.git"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "echo \"Error: run tests from root\" && exit 1"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/alibaba/ice/issues"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
import miniappRenderer from './miniappRenderer';
|
||||
|
||||
export default miniappRenderer;
|
|
@ -0,0 +1,42 @@
|
|||
function miniappRenderer(
|
||||
{ appConfig, createBaseApp, createHistory, staticConfig, pageProps, emitLifeCycles },
|
||||
{ mount, unmount, createElement, Component }
|
||||
) {
|
||||
const history = createHistory({ routes: staticConfig.routes });
|
||||
createBaseApp(appConfig);
|
||||
const rootId = appConfig.app.rootId;
|
||||
|
||||
emitLifeCycles();
|
||||
class App extends Component {
|
||||
public render() {
|
||||
const { Page, ...otherProps } = this.props;
|
||||
return createElement(Page, otherProps);
|
||||
}
|
||||
}
|
||||
|
||||
(window as any).__pagesRenderInfo = staticConfig.routes.map(({ source, component }: any) => {
|
||||
return {
|
||||
path: source,
|
||||
render() {
|
||||
const PageComponent = component()();
|
||||
const rootEl = document.createElement('div');
|
||||
rootEl.setAttribute('id', rootId);
|
||||
document.body.appendChild(rootEl);
|
||||
const appInstance = mount(createElement(App, {
|
||||
history,
|
||||
location: history.location,
|
||||
...pageProps,
|
||||
Page: PageComponent
|
||||
}), rootEl);
|
||||
|
||||
(document as any).__unmount = unmount(appInstance, rootEl);
|
||||
},
|
||||
setDocument(value) {
|
||||
// eslint-disable-next-line no-global-assign
|
||||
document = value;
|
||||
}
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
export default miniappRenderer;
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"extends": "../../tsconfig.settings.json",
|
||||
"compilerOptions": {
|
||||
"baseUrl": "./",
|
||||
"rootDir": "src",
|
||||
"outDir": "lib"
|
||||
},
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
# plugin-app-core
|
||||
|
||||
The core plugin for icejs and raxjs.
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "build-plugin-ice-core",
|
||||
"version": "1.6.4-alpha.0",
|
||||
"description": "the core plugin for icejs.",
|
||||
"name": "build-plugin-app-core",
|
||||
"version": "0.1.6",
|
||||
"description": "the core plugin for icejs and raxjs.",
|
||||
"author": "ice-admin@alibaba-inc.com",
|
||||
"homepage": "",
|
||||
"license": "MIT",
|
||||
|
@ -20,12 +20,21 @@
|
|||
"test": "echo \"Error: run tests from root\" && exit 1"
|
||||
},
|
||||
"dependencies": {
|
||||
"chokidar": "^3.3.1",
|
||||
"deepmerge": "^4.2.2",
|
||||
"chokidar": "^3.4.1",
|
||||
"chalk": "^4.0.0",
|
||||
"create-app-shared": "0.1.5",
|
||||
"ejs": "^3.0.1",
|
||||
"fs-extra": "^8.1.0",
|
||||
"globby": "^11.0.0",
|
||||
"prettier": "^2.0.2"
|
||||
"history": "^4.9.0",
|
||||
"miniapp-history": "^0.1.0",
|
||||
"miniapp-renderer": "0.1.3",
|
||||
"prettier": "^2.0.2",
|
||||
"query-string": "^6.13.1",
|
||||
"rax-use-router": "^3.0.0",
|
||||
"rax-app-renderer": "^0.1.0",
|
||||
"react-app-renderer": "0.1.2",
|
||||
"universal-env": "^3.0.0"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
|
@ -0,0 +1,15 @@
|
|||
import setAlias from './setAlias';
|
||||
import setProjectType from './setProjectType';
|
||||
import setEntry from './setEntry';
|
||||
import setTempDir from './setTempDir';
|
||||
import setRegisterMethod from './setRegisterMethod';
|
||||
import setRegisterUserConfig from './setRegisterUserConfig';
|
||||
|
||||
export {
|
||||
setAlias,
|
||||
setProjectType,
|
||||
setEntry,
|
||||
setTempDir,
|
||||
setRegisterMethod,
|
||||
setRegisterUserConfig
|
||||
};
|
|
@ -0,0 +1,19 @@
|
|||
import * as path from 'path';
|
||||
import { ICE_TEMP } from '../constant';
|
||||
|
||||
export default (api, options) => {
|
||||
const { onGetWebpackConfig, context, getValue } = api;
|
||||
const { rootDir } = context;
|
||||
const tempPath = getValue(ICE_TEMP);
|
||||
const aliasKey = options.framework === 'rax' ? 'rax-app' : 'ice';
|
||||
const aliasMap = [
|
||||
[`${aliasKey}$`, path.join(tempPath, 'index.ts')],
|
||||
[`${aliasKey}`, path.join(tempPath, 'pages') ],
|
||||
['@', path.join(rootDir, 'src')],
|
||||
['aliasKey', path.join(tempPath)]
|
||||
];
|
||||
|
||||
onGetWebpackConfig((config: any) => {
|
||||
aliasMap.forEach(alias => config.resolve.alias.set(alias[0], alias[1]));
|
||||
});
|
||||
};
|
|
@ -0,0 +1,10 @@
|
|||
export default (api, options) => {
|
||||
const { modifyUserConfig, context } = api;
|
||||
const { userConfig } = context;
|
||||
|
||||
if (options.framework === 'react') {
|
||||
if (!userConfig.entry) {
|
||||
modifyUserConfig('entry', 'src/app');
|
||||
}
|
||||
}
|
||||
};
|
|
@ -0,0 +1,13 @@
|
|||
import * as globby from 'globby';
|
||||
import { PROJECT_TYPE } from '../constant';
|
||||
|
||||
export default (api) => {
|
||||
const { context, setValue } = api;
|
||||
const { rootDir } = context;
|
||||
const tsEntryFiles = globby.sync(['src/app.@(ts?(x))', 'src/pages/*/app.@(ts?(x))'], {
|
||||
cwd: rootDir
|
||||
});
|
||||
const projectType = tsEntryFiles.length ? 'ts' : 'js';
|
||||
|
||||
setValue(PROJECT_TYPE, projectType);
|
||||
};
|
|
@ -0,0 +1,45 @@
|
|||
import getPages from '../utils/getPages';
|
||||
import getRoutes from '../utils/getRoutes';
|
||||
import formatPath from '../utils/formatPath';
|
||||
import { getExportApiKeys } from '../constant';
|
||||
|
||||
export default (api, options) => {
|
||||
const { registerMethod } = api;
|
||||
const { generator } = options;
|
||||
|
||||
// register utils method
|
||||
registerMethod('getPages', getPages);
|
||||
registerMethod('formatPath', formatPath);
|
||||
registerMethod('getRoutes', getRoutes);
|
||||
|
||||
// registerMethod for modify page
|
||||
registerMethod('addPageExport', generator.addPageExport);
|
||||
registerMethod('removePageExport', generator.removePageExport);
|
||||
|
||||
// registerMethod for add export
|
||||
const apiKeys = getExportApiKeys();
|
||||
apiKeys.forEach((apiKey) => {
|
||||
registerMethod(apiKey, (exportData) => {
|
||||
generator.addExport(apiKey, exportData);
|
||||
});
|
||||
registerMethod(apiKey.replace('add', 'remove'), (removeExportName) => {
|
||||
generator.removeExport(apiKey, removeExportName);
|
||||
});
|
||||
});
|
||||
|
||||
const registerAPIs = {
|
||||
addEntryImports: {
|
||||
apiKey: 'addContent',
|
||||
},
|
||||
addEntryCode: {
|
||||
apiKey: 'addContent',
|
||||
},
|
||||
};
|
||||
|
||||
Object.keys(registerAPIs).forEach((apiName) => {
|
||||
registerMethod(apiName, (code, position = 'after') => {
|
||||
const { apiKey } = registerAPIs[apiName];
|
||||
generator[apiKey](apiName, code, position);
|
||||
});
|
||||
});
|
||||
};
|
|
@ -0,0 +1,6 @@
|
|||
import { USER_CONFIG } from '../constant';
|
||||
|
||||
export default (api) => {
|
||||
const { registerUserConfig } = api;
|
||||
USER_CONFIG.forEach(item => registerUserConfig({ ...item }));
|
||||
};
|
|
@ -0,0 +1,18 @@
|
|||
import * as path from 'path';
|
||||
import * as fse from 'fs-extra';
|
||||
import { ICE_TEMP } from '../constant';
|
||||
|
||||
export default (api, options) => {
|
||||
const { context, setValue } = api;
|
||||
const { rootDir } = context;
|
||||
|
||||
const { framework } = options;
|
||||
const isRax = framework === 'rax';
|
||||
|
||||
const tempDir = isRax ? 'rax' : 'ice';
|
||||
const tempPath = path.join(rootDir, `.${tempDir}`);
|
||||
setValue(ICE_TEMP, tempPath);
|
||||
|
||||
fse.ensureDirSync(tempPath);
|
||||
fse.emptyDirSync(tempPath);
|
||||
};
|
|
@ -0,0 +1,57 @@
|
|||
export const USER_CONFIG = [
|
||||
{
|
||||
name: 'store',
|
||||
validation: 'boolean'
|
||||
},
|
||||
{
|
||||
name: 'ssr',
|
||||
validation: 'boolean'
|
||||
},
|
||||
{
|
||||
name: 'targets',
|
||||
validation: 'array'
|
||||
},
|
||||
{
|
||||
name: 'miniapp',
|
||||
validation: 'object'
|
||||
}
|
||||
];
|
||||
|
||||
export const PROJECT_TYPE = 'PROJECT_TYPE';
|
||||
export const ICE_TEMP = 'ICE_TEMP';
|
||||
|
||||
/**
|
||||
* API_Names
|
||||
*
|
||||
* deprecated api
|
||||
* addIceExport、addIceTypesExport、addIceAppConfigTypes、addIceAppConfigAppTypes
|
||||
*
|
||||
* for ice and rax
|
||||
* addExport、addTypesExport、addAppConfigTypes、addAppConfigAppTypes
|
||||
*/
|
||||
export const EXPORT_API_MPA = [
|
||||
{
|
||||
name: ['addIceExport', 'addExport'],
|
||||
value: ['imports', 'exports']
|
||||
},
|
||||
{
|
||||
name: ['addIceTypesExport', 'addTypesExport'],
|
||||
value: ['typesImports', 'typesExports'],
|
||||
},
|
||||
{
|
||||
name: ['addIceAppConfigTypes', 'addAppConfigTypes'],
|
||||
value: ['appConfigTypesImports', 'appConfigTypesExports']
|
||||
},
|
||||
{
|
||||
name: ['addIceAppConfigAppTypes', 'addAppConfigAppTypes'],
|
||||
value: ['appConfigAppTypesImports', 'appConfigAppTypesExports']
|
||||
}
|
||||
];
|
||||
|
||||
export const getExportApiKeys = () => {
|
||||
let apiKeys = [];
|
||||
EXPORT_API_MPA.forEach(item => {
|
||||
apiKeys = apiKeys.concat(item.name);
|
||||
});
|
||||
return apiKeys;
|
||||
};
|
|
@ -0,0 +1,36 @@
|
|||
import * as path from 'path';
|
||||
import * as chokidar from 'chokidar';
|
||||
|
||||
export default (api, options: any = {}) => {
|
||||
const { registerMethod, context } = api;
|
||||
const { rootDir } = context;
|
||||
const { render } = options;
|
||||
|
||||
const watchEvents = [];
|
||||
registerMethod('watchFileChange', (pattern, action) => {
|
||||
watchEvents.push([pattern, action]);
|
||||
});
|
||||
chokidar.watch(path.join(rootDir, 'src'), {
|
||||
ignoreInitial: true,
|
||||
}).on('all', (event, filePath) => {
|
||||
watchEvents.forEach(([pattern, action]) => {
|
||||
if (pattern instanceof RegExp && pattern.test(filePath)) {
|
||||
action(event, filePath);
|
||||
} else if (typeof pattern === 'string' && filePath.includes(pattern)) {
|
||||
action(event, filePath);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// watch pages change
|
||||
watchEvents.push([/src\/pages\/[A-Za-z.$]+$/, () => {
|
||||
render();
|
||||
}]);
|
||||
|
||||
// rerender when global style file added or removed
|
||||
watchEvents.push([/src\/global.(scss|less|css)/, async (event: string) => {
|
||||
if (event === 'unlink' || event === 'add') {
|
||||
await render();
|
||||
}
|
||||
}]);
|
||||
};
|
|
@ -0,0 +1,244 @@
|
|||
import * as path from 'path';
|
||||
import * as fse from 'fs-extra';
|
||||
import * as globby from 'globby';
|
||||
import * as ejs from 'ejs';
|
||||
import * as prettier from 'prettier';
|
||||
import generateExports from '../utils/generateExports';
|
||||
import checkExportData from '../utils/checkExportData';
|
||||
import removeExportData from '../utils/removeExportData';
|
||||
import getPages from '../utils/getPages';
|
||||
import { IExportData } from '../types/base';
|
||||
import { getExportApiKeys, EXPORT_API_MPA } from '../constant';
|
||||
|
||||
interface IRenderData {
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
interface IRegistration {
|
||||
[key: string]: any[];
|
||||
}
|
||||
|
||||
interface IRenderFile {
|
||||
(templatePath: string, targetDir: string, extraData?: IRenderData): void;
|
||||
}
|
||||
|
||||
interface IPageRenderData {
|
||||
pageImports: string;
|
||||
pageExports: string;
|
||||
}
|
||||
|
||||
export default class Generator {
|
||||
public templatesDir: string;
|
||||
|
||||
public appTemplateDir: string;
|
||||
|
||||
public commonTemplateDir: string;
|
||||
|
||||
public targetDir: string;
|
||||
|
||||
public renderData: IRenderData;
|
||||
|
||||
public contentRegistration: IRegistration;
|
||||
|
||||
private rerender: boolean;
|
||||
|
||||
private rootDir: string;
|
||||
|
||||
private pageExports: { [key: string]: IExportData[] };
|
||||
|
||||
private pageRenderData: IPageRenderData;
|
||||
|
||||
private log: any;
|
||||
|
||||
private showPrettierError: boolean;
|
||||
|
||||
constructor({ rootDir, targetDir, templatesDir, appTemplateDir, commonTemplateDir, defaultData, log }) {
|
||||
this.rootDir = rootDir;
|
||||
this.templatesDir = templatesDir;
|
||||
this.appTemplateDir = appTemplateDir;
|
||||
this.commonTemplateDir = commonTemplateDir;
|
||||
this.targetDir = targetDir;
|
||||
this.renderData = defaultData;
|
||||
this.contentRegistration = {};
|
||||
this.rerender = false;
|
||||
this.log = log;
|
||||
this.showPrettierError = true;
|
||||
this.pageExports = {};
|
||||
this.pageRenderData = { pageImports: '', pageExports: '' };
|
||||
}
|
||||
|
||||
public addExport = (registerKey, exportData: IExportData | IExportData[]) => {
|
||||
const exportList = this.contentRegistration[registerKey] || [];
|
||||
checkExportData(exportList, exportData, registerKey);
|
||||
this.addContent(registerKey, exportData);
|
||||
if (this.rerender) {
|
||||
this.render();
|
||||
}
|
||||
}
|
||||
|
||||
public removeExport = (registerKey: string, removeExportName: string | string[]) => {
|
||||
const exportList = this.contentRegistration[registerKey] || [];
|
||||
this.contentRegistration[registerKey] = removeExportData(exportList, removeExportName);
|
||||
if (this.rerender) {
|
||||
this.render();
|
||||
}
|
||||
}
|
||||
|
||||
// addEntryImports/addEntryCode
|
||||
public addContent(apiName, ...args) {
|
||||
const apiKeys = getExportApiKeys();
|
||||
if (!apiKeys.includes(apiName)) {
|
||||
throw new Error(`invalid API ${apiName}`);
|
||||
}
|
||||
const [data, position] = args;
|
||||
if (position && !['before', 'after'].includes(position)) {
|
||||
throw new Error(`invalid position ${position}, use before|after`);
|
||||
}
|
||||
const registerKey = position ? `${apiName}_${position}` : apiName;
|
||||
if (!this.contentRegistration[registerKey]) {
|
||||
this.contentRegistration[registerKey] = [];
|
||||
}
|
||||
const content = Array.isArray(data) ? data : [data];
|
||||
this.contentRegistration[registerKey].push(...content);
|
||||
}
|
||||
|
||||
private getExportStr(registerKey, dataKeys) {
|
||||
const exportList = this.contentRegistration[registerKey] || [];
|
||||
const { importStr, exportStr } = generateExports(exportList);
|
||||
const [importStrKey, exportStrKey] = dataKeys;
|
||||
return {
|
||||
[importStrKey]: importStr,
|
||||
[exportStrKey]: exportStr
|
||||
};
|
||||
}
|
||||
|
||||
public parseRenderData() {
|
||||
const staticConfig = globby.sync(['src/app.json'], { cwd: this.rootDir });
|
||||
const globalStyles = globby.sync(['src/global.@(scss|less|css)'], { cwd: this.rootDir });
|
||||
let exportsData = {};
|
||||
EXPORT_API_MPA.forEach(item => {
|
||||
item.name.forEach(key => {
|
||||
const data = this.getExportStr(key, item.value);
|
||||
exportsData = Object.assign({}, exportsData, data);
|
||||
});
|
||||
});
|
||||
this.renderData = {
|
||||
...this.renderData,
|
||||
...this.pageRenderData,
|
||||
...exportsData,
|
||||
staticConfig: staticConfig.length && staticConfig[0],
|
||||
globalStyle: globalStyles.length && globalStyles[0],
|
||||
entryImportsBefore: this.generateImportStr('addEntryImports_before'),
|
||||
entryImportsAfter: this.generateImportStr('addEntryImports_after'),
|
||||
entryCodeBefore: this.contentRegistration.addEntryCode_before || '',
|
||||
entryCodeAfter: this.contentRegistration.addEntryCode_after || '',
|
||||
};
|
||||
}
|
||||
|
||||
public generateImportStr(apiName) {
|
||||
const imports = this.contentRegistration[apiName] || [];
|
||||
return imports.map(({ source, specifier }) => {
|
||||
return specifier ?
|
||||
`import ${specifier} from '${source}';` : `import '${source}'`;
|
||||
}).join('\n');
|
||||
}
|
||||
|
||||
public async render() {
|
||||
this.rerender = true;
|
||||
const appTemplates = await globby(['**/*'], { cwd: this.appTemplateDir });
|
||||
this.parseRenderData();
|
||||
appTemplates.forEach((templateFile) => {
|
||||
if (templateFile === 'page.ts.ejs') {
|
||||
this.renderPageTemplates(templateFile);
|
||||
} else {
|
||||
this.renderAppTemplates(templateFile);
|
||||
}
|
||||
});
|
||||
|
||||
this.renderCommonTemplates();
|
||||
}
|
||||
|
||||
public async renderAppTemplates(templateFile) {
|
||||
this.renderFile(
|
||||
path.join(this.appTemplateDir, templateFile),
|
||||
path.join(this.targetDir, templateFile)
|
||||
);
|
||||
}
|
||||
|
||||
public renderPageTemplates(templateFile) {
|
||||
const pages = getPages(this.rootDir);
|
||||
pages.forEach((name) => {
|
||||
const source = `./pages/${name}/index`;
|
||||
this.pageRenderData = { ...this.getPageExport(name) };
|
||||
this.renderFile(
|
||||
path.join(this.appTemplateDir, templateFile),
|
||||
path.join(this.targetDir, `${source}.ts`), this.pageRenderData);
|
||||
});
|
||||
}
|
||||
|
||||
public async renderCommonTemplates() {
|
||||
const commonTemplates = await globby(['**/*'], { cwd: this.commonTemplateDir });
|
||||
commonTemplates.forEach((templateFile) => {
|
||||
this.renderFile(
|
||||
path.join(this.commonTemplateDir, templateFile),
|
||||
path.join(this.targetDir, templateFile)
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
public renderFile: IRenderFile = (templatePath, targetPath, extraData = {}) => {
|
||||
const renderExt = '.ejs';
|
||||
if (path.extname(templatePath) === '.ejs') {
|
||||
const templateContent = fse.readFileSync(templatePath, 'utf-8');
|
||||
let content = ejs.render(templateContent, { ...this.renderData, ...extraData });
|
||||
try {
|
||||
content = prettier.format(content, {
|
||||
parser: 'typescript',
|
||||
singleQuote: true
|
||||
});
|
||||
} catch (error) {
|
||||
if (this.showPrettierError) {
|
||||
this.log.warn(`Prettier format error: ${error.message}`);
|
||||
this.showPrettierError = false;
|
||||
}
|
||||
}
|
||||
const realTargetPath = targetPath.replace(renderExt, '');
|
||||
fse.ensureDirSync(path.dirname(realTargetPath));
|
||||
fse.writeFileSync(realTargetPath, content, 'utf-8');
|
||||
} else {
|
||||
fse.ensureDirSync(targetPath);
|
||||
fse.copyFileSync(targetPath, targetPath);
|
||||
}
|
||||
}
|
||||
|
||||
private getPageExport(pageName) {
|
||||
const exportList = this.pageExports[pageName] || [];
|
||||
const { importStr, exportStr } = generateExports(exportList);
|
||||
|
||||
return {
|
||||
pageImports: importStr,
|
||||
pageExports: exportStr,
|
||||
};
|
||||
}
|
||||
|
||||
public addPageExport = (pageName: string, exportData: IExportData | IExportData[]) => {
|
||||
if (!this.pageExports[pageName]) {
|
||||
this.pageExports[pageName] = [];
|
||||
}
|
||||
checkExportData(this.pageExports[pageName], exportData, 'addPageExport');
|
||||
this.pageExports[pageName] = [
|
||||
...this.pageExports[pageName],
|
||||
...(Array.isArray(exportData) ? exportData : [exportData]),
|
||||
];
|
||||
if (this.rerender) {
|
||||
this.render();
|
||||
}
|
||||
}
|
||||
|
||||
public removePageExport = (pageName: string, removeExportName: string | string[]) => {
|
||||
this.pageExports[pageName] = removeExportData(this.pageExports[pageName] || [], removeExportName);
|
||||
if (this.rerender) {
|
||||
this.render();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
export * from './runApp';
|
||||
|
||||
<%- imports %>
|
||||
|
||||
<% if (exports) { %>
|
||||
export {
|
||||
<%- exports %>
|
||||
}
|
||||
<% } %>
|
|
@ -0,0 +1,13 @@
|
|||
export * from './runApp';
|
||||
export { runApp as createApp } from './runApp';
|
||||
export { lazy } from './lazy';
|
||||
export * from './types';
|
||||
export const APP_MODE = (global as any).__app_mode__ || process.env.APP_MODE;
|
||||
|
||||
<%- imports %>
|
||||
|
||||
<% if (exports) { %>
|
||||
export {
|
||||
<%- exports %>
|
||||
}
|
||||
<% } %>
|
|
@ -1,6 +1,6 @@
|
|||
import * as React from 'react';
|
||||
|
||||
function lazy(dynamicImport, isRouteComponent?: Boolean): any {
|
||||
export function lazy(dynamicImport, isRouteComponent?: boolean): any {
|
||||
if (isRouteComponent) {
|
||||
return {
|
||||
__LAZY__: true,
|
||||
|
@ -10,6 +10,3 @@ function lazy(dynamicImport, isRouteComponent?: Boolean): any {
|
|||
return React.lazy(dynamicImport);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export { lazy };
|
|
@ -23,12 +23,15 @@ class ErrorBoundary extends React.Component<IProps, IState> {
|
|||
Fallback: ErrorBoundaryFallback,
|
||||
};
|
||||
|
||||
state = {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
error: null,
|
||||
info: {
|
||||
componentStack: ''
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
componentDidCatch(error: Error, info: IErrorInfo): void {
|
||||
const { onError } = this.props;
|
||||
|
@ -65,5 +68,3 @@ class ErrorBoundary extends React.Component<IProps, IState> {
|
|||
}
|
||||
|
||||
export default ErrorBoundary;
|
||||
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
let appConfig;
|
||||
|
||||
function setAppConfig(config) {
|
||||
appConfig = config;
|
||||
}
|
||||
|
||||
function getAppConfig() {
|
||||
return appConfig;
|
||||
}
|
||||
|
||||
export {
|
||||
setAppConfig,
|
||||
getAppConfig
|
||||
};
|
|
@ -0,0 +1,11 @@
|
|||
function loadRuntimeModules(runtime) {
|
||||
<% if (runtimeModules.length) {%>
|
||||
<% runtimeModules.forEach((runtimeModule) => { %>
|
||||
<% if(!runtimeModule.staticModule){ %>
|
||||
runtime.loadModule(require('<%= runtimeModule.path %>'));
|
||||
<% } %>
|
||||
<% }) %>
|
||||
<% } %>
|
||||
}
|
||||
|
||||
export default loadRuntimeModules;
|
|
@ -0,0 +1,11 @@
|
|||
function loadStaticModules(appConfig) {
|
||||
<% if (runtimeModules.length) {%>
|
||||
<% runtimeModules.forEach((runtimeModule) => { %>
|
||||
<% if(runtimeModule.staticModule){ %>
|
||||
require('<%= runtimeModule.path %>').default({appConfig});
|
||||
<% } %>
|
||||
<% }) %>
|
||||
<% } %>
|
||||
}
|
||||
|
||||
export default loadStaticModules;
|
|
@ -0,0 +1,24 @@
|
|||
<% if(isRax){ %>
|
||||
import { render } from 'rax';
|
||||
import DriverUniversal from 'driver-universal';
|
||||
|
||||
export function mount(appInstance, rootEl) {
|
||||
return render(appInstance, rootEl, {
|
||||
driver: DriverUniversal
|
||||
});
|
||||
}
|
||||
|
||||
export function unmount(appInstance, rootEl) {
|
||||
return appInstance._internal.unmountComponent.bind(appInstance._internal);
|
||||
}
|
||||
<% } else { %>
|
||||
import ReactDOM from 'react-dom';
|
||||
|
||||
export function mount(appInstance, rootEl) {
|
||||
return ReactDOM.render(appInstance, rootEl);
|
||||
}
|
||||
|
||||
export function unmount(appInstance, rootEl) {
|
||||
return () => ReactDOM.unmountComponentAtNode(rootEl);
|
||||
}
|
||||
<% } %>
|
|
@ -0,0 +1,97 @@
|
|||
import { createElement, useEffect, Component } from '<%- framework %>';
|
||||
import { isMiniApp, isWeChatMiniProgram, isByteDanceMicroApp } from 'universal-env';
|
||||
import miniappRenderer from 'miniapp-renderer';
|
||||
import createShareAPI from 'create-app-shared';
|
||||
|
||||
<% if (isReact) {%>
|
||||
import reactAppRenderer from 'react-app-renderer';
|
||||
import { withRouter as defaultWithRouter } from 'react-router';
|
||||
<% } %>
|
||||
|
||||
<% if (isRax) {%>
|
||||
import raxAppRenderer from 'rax-app-renderer';
|
||||
import { withRouter as defaultWithRouter } from 'rax-use-router';
|
||||
<% } %>
|
||||
|
||||
import loadRuntimeModules from './loadRuntimeModules';
|
||||
import loadStaticModules from './loadStaticModules';
|
||||
import staticConfig from './staticConfig';
|
||||
import { setAppConfig } from './appConfig';
|
||||
import ErrorBoundary from './ErrorBoundary';
|
||||
import { mount, unmount } from './render';
|
||||
|
||||
<% if (globalStyle) {%>
|
||||
import '../<%= globalStyle %>'
|
||||
<% } %>
|
||||
|
||||
const {
|
||||
createBaseApp,
|
||||
withRouter,
|
||||
createHistory,
|
||||
getHistory,
|
||||
emitLifeCycles,
|
||||
usePageShow,
|
||||
usePageHide,
|
||||
withPageLifeCycle,
|
||||
pathRedirect,
|
||||
registerNativeEventListeners,
|
||||
addNativeEventListener,
|
||||
removeNativeEventListener,
|
||||
useSearchParams,
|
||||
withSearchParams
|
||||
} = createShareAPI({
|
||||
createElement,
|
||||
useEffect,
|
||||
withRouter: defaultWithRouter
|
||||
}, loadRuntimeModules, loadStaticModules);
|
||||
|
||||
export function runApp(appConfig) {
|
||||
let renderer;
|
||||
<% if(isRax){ %>
|
||||
renderer = raxAppRenderer;
|
||||
<% } %>
|
||||
if (isMiniApp || isWeChatMiniProgram || isByteDanceMicroApp) {
|
||||
renderer = miniappRenderer;
|
||||
}<% if(isReact){ %> else {
|
||||
renderer = reactAppRenderer;
|
||||
}
|
||||
<% } %>
|
||||
renderer({
|
||||
appConfig,
|
||||
staticConfig,
|
||||
setAppConfig,
|
||||
createBaseApp,
|
||||
createHistory,
|
||||
getHistory,
|
||||
emitLifeCycles,
|
||||
pathRedirect,
|
||||
ErrorBoundary
|
||||
}, {
|
||||
createElement,
|
||||
mount,
|
||||
unmount,
|
||||
Component
|
||||
})
|
||||
};
|
||||
|
||||
// Public API
|
||||
export {
|
||||
// router api
|
||||
withRouter,
|
||||
getHistory,
|
||||
useSearchParams,
|
||||
withSearchParams,
|
||||
|
||||
// LifeCycles api
|
||||
usePageShow,
|
||||
usePageHide,
|
||||
withPageLifeCycle,
|
||||
|
||||
// events api
|
||||
registerNativeEventListeners,
|
||||
addNativeEventListener,
|
||||
removeNativeEventListener,
|
||||
|
||||
// components
|
||||
ErrorBoundary,
|
||||
};
|
|
@ -0,0 +1,11 @@
|
|||
let staticConfig = {};
|
||||
|
||||
try {
|
||||
<% if (staticConfig) {%>
|
||||
staticConfig = require('../<%= staticConfig %>');
|
||||
<% } %>
|
||||
} catch (error) {
|
||||
// ignore error
|
||||
}
|
||||
|
||||
export default staticConfig.__esModule ? staticConfig.default : staticConfig;
|
|
@ -0,0 +1,44 @@
|
|||
import React from 'react';
|
||||
<%- appConfigTypesImports %>
|
||||
<%- appConfigAppTypesImports %>
|
||||
<%- typesImports %>
|
||||
|
||||
interface IOnTabItemClickParams {
|
||||
from: string;
|
||||
path: string;
|
||||
text: string;
|
||||
index: number;
|
||||
}
|
||||
|
||||
export interface IApp {
|
||||
rootId?: string;
|
||||
mountNode?: HTMLElement;
|
||||
addProvider?: ({ children }: { children: React.ReactNode }) => React.ReactElement;
|
||||
getInitialData?: () => Promise<any>;
|
||||
ErrorBoundaryFallback?: React.ComponentType,
|
||||
onErrorBoundaryHander?: (error: Error, componentStack: string) => any;
|
||||
onLaunch?: () => any;
|
||||
onShow?: () => any;
|
||||
onHide?: () => any;
|
||||
onError?: (error: Error) => any;
|
||||
onTabItemClick?: ({ from, path, text, index }: IOnTabItemClickParams) => any;
|
||||
<% if (appConfigAppTypesImports) { %>
|
||||
<%- appConfigAppTypesExports %>
|
||||
<% } %>
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
<% if (appConfigTypesImports) { %>
|
||||
export interface IAppConfig {
|
||||
app?: IApp
|
||||
<%- appConfigTypesExports %>
|
||||
}
|
||||
<% } %>
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
__ICE_SSR_ENABLED__: any;
|
||||
__ICE_APP_DATA__: any;
|
||||
__ICE_PAGE_PROPS__: any;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,116 @@
|
|||
|
||||
import * as path from 'path';
|
||||
import Generator from './generator';
|
||||
import getRuntimeModules from './utils/getRuntimeModules';
|
||||
import { ICE_TEMP } from './constant';
|
||||
import dev from './dev';
|
||||
import { setAlias, setProjectType, setEntry, setTempDir, setRegisterMethod, setRegisterUserConfig } from './config';
|
||||
|
||||
// eslint-disable-next-line
|
||||
const chalk = require('chalk');
|
||||
|
||||
export default (api, options) => {
|
||||
const { onHook, context } = api;
|
||||
const { command, userConfig } = context;
|
||||
const { targets } = userConfig;
|
||||
|
||||
// Check target
|
||||
checkTargets(targets);
|
||||
|
||||
// Set temporary directory
|
||||
// eg: .ice or .rax
|
||||
setTempDir(api, options);
|
||||
|
||||
// Set project type
|
||||
// eg: ts | js
|
||||
setProjectType(api);
|
||||
|
||||
// Modify default entry to src/app
|
||||
// eg: src/app.(ts|js)
|
||||
setEntry(api, options);
|
||||
|
||||
// Set alias
|
||||
setAlias(api, options);
|
||||
|
||||
// register config in build.json
|
||||
setRegisterUserConfig(api);
|
||||
|
||||
// register api method
|
||||
const generator = initGenerator(api, options);
|
||||
setRegisterMethod(api, { generator });
|
||||
|
||||
// watch src folder
|
||||
if (command === 'start') {
|
||||
dev(api, { render: generator.render });
|
||||
}
|
||||
|
||||
onHook(`before.${command}.run`, async () => {
|
||||
await generator.render();
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
function initGenerator(api, options) {
|
||||
const { getAllPlugin, context, log, getValue } = api;
|
||||
const { userConfig, rootDir } = context;
|
||||
const { framework } = options;
|
||||
const plugins = getAllPlugin();
|
||||
const templatesDir = path.join(__dirname, './generator/templates');
|
||||
const { targets = [] } = userConfig;
|
||||
const isMiniapp = targets.includes('miniapp') || targets.includes('wechat-miniprogram');
|
||||
return new Generator({
|
||||
rootDir,
|
||||
targetDir: getValue(ICE_TEMP),
|
||||
templatesDir,
|
||||
appTemplateDir: path.join(templatesDir, `./app/${framework}`),
|
||||
commonTemplateDir: path.join(templatesDir, './common'),
|
||||
defaultData: {
|
||||
framework,
|
||||
isReact: framework === 'react',
|
||||
isRax: framework === 'rax',
|
||||
isMiniapp,
|
||||
runtimeModules: getRuntimeModules(plugins),
|
||||
buildConfig: JSON.stringify(userConfig)
|
||||
},
|
||||
log
|
||||
});
|
||||
}
|
||||
|
||||
function checkTargets(targets) {
|
||||
let hasError = false;
|
||||
|
||||
if (Object.prototype.toString.call(targets) === '[object Object]') {
|
||||
hasError = true;
|
||||
}
|
||||
|
||||
if (typeof targets === 'string') {
|
||||
hasError = true;
|
||||
}
|
||||
|
||||
if (Array.isArray(targets) && !matchTargets(targets)) {
|
||||
hasError = true;
|
||||
}
|
||||
|
||||
if (hasError) {
|
||||
const msg = `
|
||||
targets must be the array type in build.json.
|
||||
|
||||
e.g. { "targets": ["miniapp", "wechat-miniprogram"] }
|
||||
|
||||
if you want to describes the browserslist environments for your project.
|
||||
you should set browserslist in build.json.
|
||||
|
||||
e.g. { "browserlist": { "chrome": "58", "ie": 11 } }
|
||||
`;
|
||||
console.log();
|
||||
console.log(chalk.red(msg));
|
||||
console.log();
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
function matchTargets(targets) {
|
||||
return targets.every(target => {
|
||||
return ['web', 'miniapp', 'wechat-miniprogram'].includes(target);
|
||||
});
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
import * as React from 'react';
|
||||
import * as queryString from 'query-string';
|
||||
// @ts-ignore
|
||||
import { ErrorBoundary } from 'ice';
|
||||
|
||||
const module = ({ addProvider, appConfig, wrapperRouteComponent }) => {
|
||||
const { app = {} } = appConfig;
|
||||
const { ErrorBoundaryFallback, onErrorBoundaryHander, parseSearchParams } = app;
|
||||
|
||||
const wrapperPageComponent = (PageComponent) => {
|
||||
const { pageConfig = {} } = PageComponent;
|
||||
const WrapperedPageComponent = (props) => {
|
||||
const searchParams = getSearchParams(parseSearchParams, props.location.search);
|
||||
if (pageConfig.errorBoundary) {
|
||||
return (
|
||||
<ErrorBoundary Fallback={ErrorBoundaryFallback} onError={onErrorBoundaryHander}>
|
||||
<PageComponent {... Object.assign({}, props, searchParams)} />
|
||||
</ErrorBoundary>
|
||||
);
|
||||
}
|
||||
return <PageComponent {... Object.assign({}, props, searchParams)} />;
|
||||
};
|
||||
return WrapperedPageComponent;
|
||||
};
|
||||
|
||||
wrapperRouteComponent(wrapperPageComponent);
|
||||
|
||||
if (appConfig.app && appConfig.app.addProvider) {
|
||||
addProvider(appConfig.app.addProvider);
|
||||
}
|
||||
};
|
||||
|
||||
function getSearchParams(parseSearchParams, locationSearch) {
|
||||
if (parseSearchParams) {
|
||||
const searchParams = queryString.parse(locationSearch);
|
||||
return { searchParams };
|
||||
}
|
||||
}
|
||||
|
||||
export default module;
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue