feat: support lazy store (#236)

* feat: support lazy store

* feat: support component model

* fix: typo

* feat: support store api

* chore: lazy example

* feat: add babelPluginReplacePath

* feat: replace router component path

* feat: add getRoutes util

* feat: support custom routes path

* feat: support replace custom alias

* fix: only transform Import node

* fix: alias key

* fix: only transform pages

* fix: set default alias

* fix: match pages

* fix: calculate relative path

Co-authored-by: 许文涛 <alvin.hui@qq.com>
This commit is contained in:
chenbin92 2020-04-17 12:13:37 +08:00 committed by GitHub
parent c85e2491bc
commit a787dd099b
27 changed files with 493 additions and 143 deletions

View File

@ -0,0 +1,6 @@
{
"plugins": [],
"router": {
"lazy": false
}
}

View File

@ -34,4 +34,8 @@ const About = () => {
);
};
About.pageConfig = {
title: 'About'
};
export default About;

View File

@ -0,0 +1,14 @@
import React from 'react';
import model from 'ice/Home/components/List/model';
const List = () => {
console.log('List Component:', model.useValue());
return (
<>
<p>List</p>
</>
);
};
export default List;

View File

@ -0,0 +1,5 @@
export default {
state: {
title: 'List Model'
},
};

View File

@ -0,0 +1,14 @@
import React from 'react';
import model from 'ice/Home/components/Todo/model';
const Todo = () => {
console.log('Todo Component:', model.useValue());
return (
<>
<p>Todo</p>
</>
);
};
export default Todo;

View File

@ -0,0 +1,5 @@
export default {
state: {
title: 'Todo Model'
},
};

View File

@ -1,6 +1,8 @@
import React from 'react';
import { Link, store as appStore } from 'ice';
import { store as pageStore } from 'ice/Home';
import Todo from './components/Todo';
import List from './components/List';
const Home = () => {
const [counterState, counterActions] = appStore.useModel('counter');
@ -16,6 +18,9 @@ const Home = () => {
<button type="button" onClick={counterActions.decrementAsync}>-</button>
</div>
<Todo />
<List />
<Link to="/about">about</Link>
</>
);

View File

@ -1,5 +1,10 @@
import Home from '@/pages/Home';
import About from '@/pages/About';
import { lazy } from 'ice';
// import Home from '@/pages/Home';
// import About from '@/pages/About';
const Home = lazy(() => import('@/pages/Home'));
const About =lazy (() => import('@/pages/About'));
export default [
{

View File

@ -6,7 +6,7 @@ import checkExportData from '../utils/checkExportData';
import removeExportData from '../utils/removeExportData';
import { IExportData } from '../types';
export default class UsePageGenerator {
export default class PageGenerator {
private generator: Generator;
private templatePath: string;

View File

@ -5,6 +5,7 @@ import * as globby from 'globby';
import Generator from './generator';
import PageGenerator from './generator/pageGenerator';
import getPages from './utils/getPages';
import getRoutes from './utils/getRoutes';
import formatPath from './utils/formatPath';
export default (api) => {
@ -143,6 +144,7 @@ export default (api) => {
// register utils method
registerMethod('getPages', getPages);
registerMethod('formatPath', formatPath);
registerMethod('getRoutes', getRoutes);
// registerMethod for modify page
registerMethod('addPageExport', pageGenerator.addPageExport);

View File

@ -0,0 +1,37 @@
import * as path from 'path';
import * as fse from 'fs-extra';
interface IParams {
rootDir: string;
tempDir: string;
configPath: string;
projectType: string;
}
interface IResult {
routesPath: string;
isConfigRoutes: boolean;
}
function getRoutes({ rootDir, tempDir, configPath, projectType }: IParams): IResult {
const routesPath = configPath
? path.join(rootDir, configPath)
: path.join(rootDir, `src/routes.${projectType}`);
// 配置式路由
const configPathExists = fse.existsSync(routesPath);
if (configPathExists) {
return {
routesPath,
isConfigRoutes: true
};
}
// 约定式路由
return {
routesPath: path.join(tempDir, `routes.${projectType}`),
isConfigRoutes: false
};
}
export default getRoutes;

View File

@ -1,13 +1,12 @@
// eslint-disable-next-line no-unused-vars
module.exports = ({ types }, { routeFile }) => {
module.exports = ({ types }, { routesPath }) => {
let hasLazyImport = false;
let importName = '';
return {
visitor: {
ImportDeclaration(nodePath, state) {
// only transform route files
const isRouteConfig = routeFile === state.filename;
if (isRouteConfig) {
const isRoutesFile = routesPath === state.filename;
if (isRoutesFile) {
const { node } = nodePath;
if (types.isStringLiteral(node.source, { value: 'ice'})) {
node.specifiers.forEach((importSpecifier) => {
@ -24,8 +23,8 @@ module.exports = ({ types }, { routeFile }) => {
},
CallExpression(nodePath, state) {
// only transform route files
const isRouteConfig = routeFile === state.filename;
if (isRouteConfig) {
const isRoutesFile = routesPath === state.filename;
if (isRoutesFile) {
const { node } = nodePath;
if (
// case import * as xxx from 'ice'; xxx.lazy

View File

@ -39,7 +39,7 @@ function ignorePath(checkPath: string, ignoreOptions: IgnoreOptions) {
export default function walker({
rootDir,
routerOptions,
routersTempPath,
routesTempPath,
pagesDir,
}) {
const { ignoreRoutes, ignorePaths = ['components'], caseSensitive } = routerOptions;
@ -84,7 +84,7 @@ export default function walker({
const layoutName = `Layout${dirArrUpper}`;
const filePath = formatPathForWin(
path.relative(
path.dirname(routersTempPath),
path.dirname(routesTempPath),
path.join(pagesDir, pageFilePath)
)
);
@ -117,10 +117,10 @@ export default function walker({
});
// amend collects
amender(rootDir, routersTempPath, routesCollect);
amender(rootDir, routesTempPath, routesCollect);
// generate splices
let routesSplices = splicer(routesCollect, routerOptions);
// output into .tmp
fse.outputFileSync(routersTempPath, routesSplices);
fse.outputFileSync(routesTempPath, routesSplices);
routesSplices = null;
}

View File

@ -11,26 +11,41 @@ const TEM_ROUTER_SETS = [TEM_ROUTER_COMPATIBLE];
const plugin: IPlugin = ({ context, onGetWebpackConfig, modifyUserConfig, getValue, applyMethod, registerUserConfig }) => {
const { rootDir, userConfig, command } = context;
// [enum] js or ts
const isMpa = userConfig.mpa;
const projectType = getValue('PROJECT_TYPE');
// .tmp path
const iceTempPath = getValue('ICE_TEMP');
const routersTempPath = path.join(iceTempPath, `routes.${projectType}`);
const routerOptions = (userConfig.router || {}) as IRouterOptions;
const { configPath } = routerOptions;
let routeConfigPath = configPath
? path.join(rootDir, configPath)
: path.join(rootDir, `src/routes.${projectType}`);
let { configPath } = routerOptions;
const isMpa = userConfig.mpa;
const routesTempPath = path.join(iceTempPath, `routes.${projectType}`);
// if is mpa use empty router file
if (isMpa) {
// if is mpa use empty router file
fse.writeFileSync(routersTempPath, 'export default [];', 'utf-8');
routeConfigPath = routersTempPath;
fse.writeFileSync(routesTempPath, 'export default [];', 'utf-8');
configPath = routesTempPath;
}
const hasRouteFile = fse.existsSync(routeConfigPath);
const { routesPath, isConfigRoutes } = applyMethod('getRoutes', {
rootDir,
tempDir: iceTempPath,
configPath,
projectType
});
// add babel plugins for ice lazy
modifyUserConfig('babelPlugins',
[
...(userConfig.babelPlugins as [] || []),
[
require.resolve('./babelPluginLazy'),
{ routesPath }
]
]);
// copy templates and export react-router-dom/history apis to ice
const routerTemplatesPath = path.join(__dirname, '../templates');
const routerTargetPath = path.join(iceTempPath, 'router');
const routerTargetPath = path.join(iceTempPath, 'router');
fse.ensureDirSync(routerTargetPath);
fse.copySync(routerTemplatesPath, routerTargetPath);
applyMethod('addIceExport', { source: './router' });
@ -38,14 +53,12 @@ const plugin: IPlugin = ({ context, onGetWebpackConfig, modifyUserConfig, getVal
// copy types
fse.copySync(path.join(__dirname, '../src/types/index.ts'), path.join(iceTempPath, 'router/types.ts'));
applyMethod('addIceTypesExport', { source: './router/types', specifier: '{ IAppRouterProps }', exportName: 'router?: IAppRouterProps' });
const routeFile = hasRouteFile ? routeConfigPath : routersTempPath;
// add babel plugins for ice lazy
modifyUserConfig('babelPlugins', [...(userConfig.babelPlugins as [] || []), [require.resolve('./babelPluginLazy'), { routeFile }]]);
// modify webpack config
onGetWebpackConfig((config) => {
// add alias
TEM_ROUTER_SETS.forEach(i => {
config.resolve.alias.set(i, routeFile);
config.resolve.alias.set(i, routesPath);
});
// alias for runtime/Router
config.resolve.alias.set('$ice/Router', path.join(__dirname, 'runtime/Router'));
@ -68,10 +81,10 @@ const plugin: IPlugin = ({ context, onGetWebpackConfig, modifyUserConfig, getVal
});
// do not watch folder pages when route config is exsits
if (!hasRouteFile) {
if (!isConfigRoutes) {
const routerMatch = 'src/pages';
const pagesDir = path.join(rootDir, routerMatch);
const walkerOptions = { rootDir, routerOptions, routersTempPath, pagesDir };
const walkerOptions = { rootDir, routerOptions, routesTempPath, pagesDir };
walker(walkerOptions);
if (command === 'start') {
// watch folder change when dev

View File

@ -1,9 +0,0 @@
import { createStore } from '@ice/store';
const Dashboard = createStore({});
const About = createStore({});
const PageStores = { Dashboard, About };
export default PageStores;

View File

@ -0,0 +1,115 @@
import * as path from 'path';
module.exports = ({ types: t }, { routesPath, alias }) => {
const regex = /src\/pages\/\w+(.tsx|.jsx)?(\/index(.tsx|.jsx)?)?/;
return {
visitor: {
ImportDeclaration(nodePath, state) {
const isRoutesFile = (routesPath === state.filename);
if (isRoutesFile) {
let value = nodePath.node.source.value;
if (typeof value === 'string') {
// 配置式路由
// default alias: import Home from '@/pages/Home';
// custom alias: import Home from '$pages/Home';
// relative path: import Home from '../pages/Home'
const matchedPagePath = matchRelativePath(routesPath, value) || matchAliasPath(alias, value);
if (matchedPagePath && regex.test(matchedPagePath)) {
const [, , pageName] = matchedPagePath.split('/');
// replace to: import Home from 'ice/Home/Home'
value = `ice/${pageName}/${pageName}`;
replaceWith(t, nodePath, value);
}
// 约定式路由
// e.g: import Home from '../src/pages/Home/index.tsx';
if (value.startsWith('../src/pages')) {
const [, , pageName] = value.split('/');
// replace to: import Home from './pages/Home/Home'
value = `./pages/${pageName}/${pageName}`;
replaceWith(t, nodePath, value);
}
}
}
},
CallExpression(nodePath, state) {
const isRoutesFile = (routesPath === state.filename);
if (isRoutesFile) {
if (t.isImport(nodePath.node.callee)) {
const args = nodePath.node.arguments;
for (let i = 0; i < args.length; i++) {
let value = args[i].value;
if (typeof value === 'string') {
// 配置式路由
// default alias: const Home = lazy(() => import('@/pages/Home'));
// custom alias: const Home = lazy(() => import('$pages/home));
// relative path: const Home = lazy(() => import('../pages/Home'));
const matchedPagePath = matchRelativePath(routesPath, value) || matchAliasPath(alias, value);
if (matchedPagePath && regex.test(matchedPagePath)) {
const [, , pageName] = matchedPagePath.split('/');
// replace to: const Home =lazy (() => import('ice/Home/Home'));
value = `ice/${pageName}/${pageName}`;
args[i].value = value;
}
// 约定式路由
// e.g: const Home = lazy(() => import(/* webpackChunkName: 'Home' */ '../src/pages/Home/index.tsx'));
if (value.startsWith('../src/pages')) {
const [, , pageName] = value.split('/');
// replace to: import Home from './pages/Home/Home'
value = `./pages/${pageName}/${pageName}`;
args[i].value = value;
}
}
}
}
}
},
},
};
};
interface IAlias {
[key: string]: string;
}
// enum alias:
// case1: { "@": "./src", "@pages": "./src/pages" }
// case2: { "@src": "./src", "@pages": "./src/pages" }
// case3: { "@": "./src", "@/pages": "./src/pages" }
function matchAliasPath(alias: IAlias, value: string): string {
let aliasPath = '';
// use default alias
if (!Object.keys(alias).length) {
alias['@'] = 'src';
}
// use custom alias
Object.keys(alias).forEach(currKey => {
if (value.startsWith(currKey)) {
const [, ...args] = value.split(currKey);
const currAliasPath = path.join(alias[currKey], ...args);
if (currAliasPath.includes('src/pages')) {
aliasPath = currAliasPath;
}
}
});
return aliasPath;
}
function matchRelativePath(routesPath: string, value: string) {
let relativePath = '';
if (/^(\.\/|\.{2}\/)/.test(value)) {
relativePath = path.relative(process.cwd(), path.join(routesPath, '..', value));
}
return relativePath;
}
function replaceWith(t, nodePath, value) {
nodePath.replaceWith(
t.ImportDeclaration(
nodePath.node.specifiers,
t.stringLiteral(value)
)
);
}

View File

@ -4,10 +4,12 @@ import * as ejs from 'ejs';
import * as recursiveReaddir from 'fs-readdir-recursive';
import * as prettier from 'prettier';
export interface IExportData {
specifier?: string;
source: string;
exportName: string;
export interface IRenderPageParams {
pageName: string;
pageNameDir: string;
pageModelsDir: string;
pageModelFile: string;
pageComponentFiles: [];
}
export default class Generator {
@ -17,8 +19,6 @@ export default class Generator {
private pageStoreTemplatePath: string
private pageStoresTemplatePath: string
private targetPath: string
private projectType: string
@ -29,7 +29,6 @@ export default class Generator {
rootDir,
appStoreTemplatePath,
pageStoreTemplatePath,
pageStoresTemplatePath,
targetPath,
applyMethod,
projectType
@ -45,13 +44,12 @@ export default class Generator {
this.rootDir = rootDir;
this.appStoreTemplatePath = appStoreTemplatePath;
this.pageStoreTemplatePath = pageStoreTemplatePath;
this.pageStoresTemplatePath = pageStoresTemplatePath;
this.targetPath = targetPath;
this.applyMethod = applyMethod;
this.projectType = projectType;
}
private getPageModels (pageName: string, pageModelsDir: string, pageModelFile: string) {
private getPageModels(pageName: string, pageModelsDir: string, pageModelFile: string) {
if (fse.pathExistsSync(pageModelsDir)) {
const pageModels = recursiveReaddir(pageModelsDir).map(item => path.parse(item));
@ -82,8 +80,19 @@ export default class Generator {
};
}
private getComponentModels(componentFiles: []) {
const componentModels = [];
componentFiles.forEach(componentFile => {
const parsedPath = path.parse(componentFile);
if (parsedPath.name === 'model') {
componentModels.push(parsedPath);
}
});
return componentModels;
}
private renderAppStore() {
const sourceFilename = 'appStore';
const sourceFilename = 'store/index';
const exportName = 'store';
const targetPath = path.join(this.targetPath, `${sourceFilename}.ts`);
@ -106,50 +115,72 @@ export default class Generator {
this.applyMethod('addIceExport', { source: `./${sourceFilename}`, exportName });
}
private renderPageStores() {
const pages = this.applyMethod('getPages', this.rootDir);
const pageStores = [];
// generate .ice/pages/*/store.ts
pages.forEach(pageName => {
private renderPageStore({ pageName, pageNameDir, pageModelsDir, pageModelFile }: IRenderPageParams) {
if (fse.pathExistsSync(pageModelsDir) || fse.pathExistsSync(pageModelFile)) {
const sourceFilename = 'store';
const exportName = 'store';
const targetPath = path.join(this.targetPath, 'pages', pageName, `${sourceFilename}.ts`);
const pageNameDir = path.join(this.rootDir, 'src', 'pages', pageName);
// example: src/pages/*/models/*
const pageModelsDir = path.join(pageNameDir, 'models');
const pageModelFilePath = path.join(pageNameDir, 'model');
const renderData = this.getPageModels(pageName, pageModelsDir, pageModelFilePath);
this.renderFile(this.pageStoreTemplatePath, targetPath, renderData);
// example: src/pages/*/model.ts
const pageModelFile = path.join(pageNameDir, `model.${this.projectType}`);
if (fse.pathExistsSync(pageModelsDir) || fse.pathExistsSync(pageModelFile)) {
pageStores.push(pageName);
const pageModelFilePath = path.join(pageNameDir, 'model');
const renderData = this.getPageModels(pageName, pageModelsDir, pageModelFilePath);
this.renderFile(this.pageStoreTemplatePath, targetPath, renderData);
const exportName = 'store';
this.applyMethod('removePageExport', pageName, exportName);
this.applyMethod('addPageExport', pageName, { source: `./${sourceFilename}`, exportName });
}
});
// generate .ice/pageStores.ts
this.generatePageStores(pageStores);
this.applyMethod('removePageExport', pageName, exportName);
this.applyMethod('addPageExport', pageName, { source: `./${sourceFilename}`, exportName });
}
}
private generatePageStores(pageStores: string[]) {
const targetPath = path.join(this.targetPath, 'pageStores.ts');
private renderPageComponent({ pageName, pageNameDir, pageModelsDir, pageModelFile, pageComponentFiles }: IRenderPageParams) {
const pageComponentTemplatePath = path.join(__dirname, './template/pageComponent.tsx.ejs');
const pageComponentTargetPath = path.join(this.targetPath, 'pages', pageName, `${pageName}.tsx`);
const pageComponentSourcePath = this.applyMethod('formatPath', pageNameDir);
let importPageStoreStr = '';
let pageStoreStr = '';
pageStores.forEach(name => {
importPageStoreStr += `\nimport ${name} from './pages/${name}/store';`;
pageStoreStr += `${name},`;
const pageComponentRenderData = {
pageComponentImport: `import ${pageName} from '${pageComponentSourcePath}'` ,
pageComponentExport: pageName,
hasPageStore: false,
hasComponentStore: false
};
if (fse.pathExistsSync(pageModelsDir) || fse.pathExistsSync(pageModelFile)) {
pageComponentRenderData.hasPageStore = true;
}
if (this.getComponentModels(pageComponentFiles).length) {
pageComponentRenderData.hasComponentStore = true;
}
this.renderFile(pageComponentTemplatePath, pageComponentTargetPath , pageComponentRenderData);
}
private renderComponentStore(pageName: string, componentDir: string, componentFiles: []) {
const componentModels = this.getComponentModels(componentFiles);
let importStr = '';
let modelsStr = '';
componentModels.forEach(componentModel => {
importStr += `\nimport ${componentModel.dir} from '${componentDir}/${componentModel.dir}/${componentModel.name}'`;
modelsStr += `${componentModel.dir},`;
});
this.renderFile(this.pageStoresTemplatePath, targetPath, { importPageStoreStr, pageStoreStr });
const templatePath = path.join(__dirname, './template/componentStore.ts.ejs');
const targetPath = path.join(this.targetPath, `pages/${pageName}/componentStore.ts`);
componentFiles.forEach(componentFile => {
const parsedPath = path.parse(componentFile);
if (parsedPath.name === 'model') {
this.renderFile(templatePath, targetPath, { importStr, modelsStr });
}
});
}
private renderComponentModel(pageName: string, componentDir: string, componentFiles: []) {
const componentModels = this.getComponentModels(componentFiles);
const templatePath = path.join(__dirname, './template/componentModel.ts.ejs');
componentModels.forEach(componentModel => {
const targetPath = path.join(this.targetPath, `pages/${pageName}/components/${componentModel.dir}/${componentModel.base}`);
this.renderFile(templatePath, targetPath, { modelName: componentModel.dir });
});
}
private renderFile(templatePath: string, targetPath: string, extraData = {}) {
@ -168,7 +199,34 @@ export default class Generator {
}
public render() {
// generate .ice/store/index.ts
this.renderAppStore();
this.renderPageStores();
const pages = this.applyMethod('getPages', this.rootDir);
pages.forEach(pageName => {
const pageNameDir = path.join(this.rootDir, 'src', 'pages', pageName);
const pageComponentDir = path.join(pageNameDir, 'components');
const pageComponentFiles = recursiveReaddir(pageComponentDir);
// e.g: src/pages/${pageName}/models/*
const pageModelsDir = path.join(pageNameDir, 'models');
// e.g: src/pages/${pageName}/model.ts
const pageModelFile = path.join(pageNameDir, `model.${this.projectType}`);
const params = { pageName, pageNameDir, pageModelsDir, pageModelFile, pageComponentFiles };
// generate .ice/pages/${pageName}/store.ts
this.renderPageStore(params);
// generate .ice/pages/${pageName}/${pageName}.tsx
this.renderPageComponent(params);
// generate .ice/pages/${pageName}/componentStore.ts
this.renderComponentStore(pageName, pageComponentDir, pageComponentFiles);
// generate .ice/pages/${pageName}/components/${componentName}/model.ts
this.renderComponentModel(pageName, pageComponentDir, pageComponentFiles);
});
}
}

View File

@ -4,8 +4,8 @@ import * as ejs from 'ejs';
import Generator from './generator';
export default async (api) => {
const { context, getValue, onHook, applyMethod, onGetWebpackConfig } = api;
const { rootDir, command } = context;
const { context, getValue, onHook, applyMethod, onGetWebpackConfig, modifyUserConfig } = api;
const { rootDir, command, userConfig } = context;
const targetPath = getValue('ICE_TEMP');
const templatePath = path.join(__dirname, 'template');
@ -15,8 +15,7 @@ export default async (api) => {
const typesTemplatePath = path.join(templatePath, 'types.ts.ejs');
const projectType = getValue('PROJECT_TYPE');
// copy types/index.ts to .ice/store/index.ts
await fse.copy(path.join(__dirname, '..', 'src/types'), path.join(targetPath, './store'));
// set IStore to IAppConfig
applyMethod('addIceTypesExport', { source: './store', specifier: '{ IStore }', exportName: 'store?: IStore' });
// render template/types.ts.ejs to .ice/store/types.ts
@ -27,6 +26,24 @@ export default async (api) => {
fse.writeFileSync(typesTargetPath, content, 'utf-8');
applyMethod('addIceTypesExport', { source: './store/types' });
// add babel plugins for ice lazy
const { configPath } = userConfig.router || {};
const { routesPath } = applyMethod('getRoutes', {
rootDir,
tempDir: targetPath,
configPath,
projectType
});
modifyUserConfig('babelPlugins',
[
...(userConfig.babelPlugins as [] || []),
[
require.resolve('./babelPluginReplacePath'),
{ routesPath, alias: userConfig.alias }
]
]
);
onGetWebpackConfig(config => {
if (command === 'build') {
config.optimization.minimizer('TerserPlugin').tap(([args]) => [
@ -38,8 +55,7 @@ export default async (api) => {
]);
}
config.resolve.alias.set('$ice/appStore', path.join(targetPath, 'appStore.ts'));
config.resolve.alias.set('$ice/pageStores', path.join(targetPath, 'pageStores.ts'));
config.resolve.alias.set('$ice/store', path.join(targetPath, 'store', 'index.ts'));
});
const gen = new Generator({

View File

@ -1,26 +1,7 @@
import * as React from 'react';
import AppStore from '$ice/appStore';
import PageStores from '$ice/pageStores';
import AppStore from '$ice/store';
const wrapperComponent = (PageComponent) => {
const { pageConfig = {} } = PageComponent;
const StoreWrapperedComponent = (props) => {
const pageComponentName = pageConfig.componentName;
const PageStore = PageStores[pageComponentName];
if (PageStore) {
return (
<PageStore.Provider initialStates={pageConfig.initialStates}>
<PageComponent {...props}/>
</PageStore.Provider>
);
}
return <PageComponent {...props} />;
};
return StoreWrapperedComponent;
};
export default ({ addProvider, wrapperRouteComponent, appConfig, context }) => {
wrapperRouteComponent(wrapperComponent);
export default ({ addProvider, appConfig, context }) => {
const StoreProvider = ({children}) => {
const storeConfig = appConfig.store || {};

View File

@ -1,20 +1,28 @@
<% if (importStr) { %>
import { createStore, IcestoreRootState, IcestoreDispatch, Models } from '@ice/store';
<%- importStr %>
import { createStore, IcestoreRootState, IcestoreDispatch, Models } from '@ice/store';
<%- importStr %>
interface AppModel extends Models {
<% modelsStr.split(',').forEach(function(model){ %>
<% if (model) { %>
<%- model %>: typeof <%- model %>;
<% } %>
<% }); %>
interface AppModel extends Models {
<% modelsStr.split(',').forEach(function(model){ %>
<% if (model) { %>
<%- model %>: typeof <%- model %>;
<% } %>
<% }); %>
}
const appModel: AppModel = { <%- modelsStr %> };
const store = createStore(appModel);
export default store;
export type IRootDispatch = IcestoreDispatch<typeof appModel>;
export type IRootState = IcestoreRootState<typeof appModel>;
<% } %>
interface IInitialStates {
[key: string]: any;
}
const appModel: AppModel = { <%- modelsStr %> };
const store = createStore(appModel);
export default store;
export type IRootDispatch = IcestoreDispatch<typeof appModel>;
export type IRootState = IcestoreRootState<typeof appModel>;
<% } %>
export interface IStore {
initialStates?: IInitialStates;
getInitialStates?: (initialData) => IInitialStates;
}

View File

@ -0,0 +1,25 @@
// .ice/pages/components/Todo/store.ts
import store from '../../componentStore'
const modelName = '<%= modelName %>';
export default {
useValue() {
return store.useModel(modelName)
},
useState() {
return store.useModelState(modelName)
},
useDispatchers() {
return store.useModelDispatchers(modelName)
},
getValue() {
return store.getModel(modelName)
},
getState() {
return store.getModelState(modelName)
},
getDispatchers() {
return store.getModelDispatchers(modelName)
}
}

View File

@ -0,0 +1,8 @@
<% if (importStr) { %>
import { createStore } from '@ice/store';
<%- importStr %>
const models = { <%- modelsStr %> }
const store = createStore(models);
export default store;
<% } %>

View File

@ -0,0 +1,53 @@
import * as React from 'react';
<%- pageComponentImport %>
<% if(hasPageStore) { %>
import store from './store';
<% } %>
<% if(hasComponentStore) { %>
import componentStore from './componentStore';
<% } %>
const PageComponentName = <%= pageComponentExport %>;
<% if(hasPageStore || hasComponentStore) { %>
let PageProvider = null;
let ComponentProvider = null;
<% if(hasPageStore) { %>
PageProvider = store.Provider;
<% } %>
<% if(hasComponentStore) { %>
ComponentProvider = componentStore.Provider;
<% } %>
const StoreWrapperedPage = () => getComponent(PageProvider, ComponentProvider);
StoreWrapperedPage.pageConfig = PageComponentName.pageConfig || {};
export default StoreWrapperedPage;
<% } else { %>
export default PageComponentName;
<% } %>
function getComponent(PageProvider, ComponentProvider) {
if (PageProvider && ComponentProvider) {
return (
<PageProvider>
<ComponentProvider>
<PageComponentName />
</ComponentProvider>
</PageProvider>
)
} else if (PageProvider) {
return (
<PageProvider>
<PageComponentName />
</PageProvider>
)
} else if (ComponentProvider) (
<ComponentProvider>
<PageComponentName />
</ComponentProvider>
)
}

View File

@ -1,5 +0,0 @@
<%- importPageStoreStr %>
const pageStores = { <%- pageStoreStr %> };
export default pageStores;

View File

@ -1,8 +0,0 @@
export interface IInitialStates {
[key: string]: any;
}
export interface IStore {
initialStates?: IInitialStates;
getInitialStates?: (initialData) => IInitialStates;
}

View File

@ -5,8 +5,7 @@
"rootDir": "src",
"outDir": "lib",
"paths": {
"$ice/appStore": ["./src/_appStore"],
"$ice/pageStores": ["./src/_pageStores"]
"$ice/store": ["./src/_store"]
}
},
}