Skip to content
This repository has been archived by the owner on Nov 17, 2022. It is now read-only.

feat: support route config type #595

Merged
merged 3 commits into from
Oct 21, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions examples/basic-project/src/pages/about.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Link, useData, useConfig, history } from 'ice';
import type { RouteConfig } from 'ice';
import { isWeb } from '@uni/env';
import url from './ice.png';

Expand All @@ -8,7 +9,7 @@ interface Data {

export default function About() {
const data = useData<Data>();
const config = useConfig();
const config = useConfig<RouteConfig>();

console.log('render About', 'data', data, 'config', config);
console.log('history in component', history);
Expand All @@ -24,7 +25,7 @@ export default function About() {
);
}

export function getConfig() {
export function getConfig(): RouteConfig {
return {
title: 'About',
meta: [
Expand Down
6 changes: 3 additions & 3 deletions examples/basic-project/src/pages/blog.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Link, useData, useConfig } from 'ice';
import { Link, useData, useConfig, defineGetConfig } from 'ice';

export default function Blog() {
const data = useData();
Expand All @@ -14,9 +14,9 @@ export default function Blog() {
);
}

export function getConfig() {
export const getConfig = defineGetConfig(() => {
return {
title: 'Blog',
auth: ['guest'],
};
}
});
6 changes: 4 additions & 2 deletions examples/with-pha/src/pages/home.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { defineGetConfig } from 'ice';

export default function Home() {
return (
<>
Expand All @@ -6,7 +8,7 @@ export default function Home() {
);
}

export function getConfig() {
export const getConfig = defineGetConfig(() => {
return {
queryParamsPassKeys: [
'questionId',
Expand All @@ -15,4 +17,4 @@ export function getConfig() {
],
title: 'Home',
};
}
});
3 changes: 3 additions & 0 deletions packages/ice/src/createService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,9 @@ async function createService({ rootDir, command, commandArgs }: CreateServiceOpt
addExportTypes: (exportData: ExportData) => {
generator.addExport('frameworkTypes', exportData);
},
addRouteTypes: (exportData: ExportData) => {
generator.addExport('routeConfigTypes', exportData);
},
addRenderFile: generator.addRenderFile,
addRenderTemplate: generator.addTemplateFiles,
modifyRenderData: generator.modifyRenderData,
Expand Down
14 changes: 9 additions & 5 deletions packages/ice/src/service/runtimeGenerator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import type {
AddExport,
RemoveExport,
AddContent,
GetExportStr,
GetExportData,
ParseRenderData,
Render,
RenderFile,
Expand Down Expand Up @@ -38,6 +38,7 @@ interface Options {
export function generateExports(exportList: ExportData[]) {
const importStatements = [];
let exportStatements = [];
let exportNames: string[] = [];
exportList.forEach(data => {
const { specifier, source, exportAlias, type } = data;
const isDefaultImport = !Array.isArray(specifier);
Expand All @@ -50,6 +51,7 @@ export function generateExports(exportList: ExportData[]) {
} else {
exportStatements.push(`${specifierStr}${symbol}`);
}
exportNames.push(specifierStr);
});
});
return {
Expand All @@ -62,6 +64,7 @@ export function generateExports(exportList: ExportData[]) {
};
*/
exportStr: exportStatements.join('\n '),
exportNames,
};
}

Expand Down Expand Up @@ -118,7 +121,7 @@ export default class Generator {
this.rerender = false;
this.renderTemplates = [];
this.renderDataRegistration = [];
this.contentTypes = ['framework', 'frameworkTypes', 'configTypes'];
this.contentTypes = ['framework', 'frameworkTypes', 'routeConfigTypes'];
// empty .ice before render
fse.emptyDirSync(path.join(rootDir, targetDir));
// add initial templates
Expand Down Expand Up @@ -166,13 +169,14 @@ export default class Generator {
this.contentRegistration[registerKey].push(...content);
};

private getExportStr: GetExportStr = (registerKey, dataKeys) => {
private getExportData: GetExportData = (registerKey, dataKeys) => {
const exportList = this.contentRegistration[registerKey] || [];
const { importStr, exportStr } = generateExports(exportList);
const { importStr, exportStr, exportNames } = generateExports(exportList);
const [importStrKey, exportStrKey] = dataKeys;
return {
[importStrKey]: importStr,
[exportStrKey]: exportStr,
exportNames,
};
};

Expand All @@ -181,7 +185,7 @@ export default class Generator {
const globalStyles = fg.sync([getGlobalStyleGlobPattern()], { cwd: this.rootDir });
let exportsData = {};
this.contentTypes.forEach(item => {
const data = this.getExportStr(item, ['imports', 'exports']);
const data = this.getExportData(item, ['imports', 'exports']);
exportsData = Object.assign({}, exportsData, {
[`${item}`]: data,
});
Expand Down
3 changes: 1 addition & 2 deletions packages/ice/templates/core/env.server.ts.ejs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
// Define process.env in top make it possible to use ICE_CORE_* in @ice/runtime, esbuild define options doesn't have the ability
// The runtime value such as __process.env.ICE_CORE_*__ will be replaced by esbuild define, so the value is real-time
<% coreEnvKeys.forEach((key) => { %>
process.env.<%= key %> = __process.env.<%= key %>__;
<% }) %>
process.env.<%= key %> = __process.env.<%= key %>__;<% }) %>
12 changes: 8 additions & 4 deletions packages/ice/templates/core/index.ts.ejs
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
<% if (globalStyle) {-%>
import '<%= globalStyle %>'
<% } -%>

import type { RouteConfig } from './types';
<%- framework.imports %>

<% if (framework.exports) { -%>
type GetConfig = () => RouteConfig;
function defineGetConfig(getConfig: GetConfig): GetConfig {
return getConfig;
}

export {
<%- framework.exports %>
defineGetConfig,
<% if (framework.exports) { -%><%- framework.exports %><% } -%>
};
<% } -%>

export * from './types';
24 changes: 10 additions & 14 deletions packages/ice/templates/core/types.ts.ejs
Original file line number Diff line number Diff line change
@@ -1,20 +1,16 @@
import type { AppConfig as DefaultAppConfig, GetAppData, AppData } from '@ice/runtime';
import type { AppConfig, GetAppData, RouteConfig as DefaultRouteConfig } from '@ice/runtime';
<%- routeConfigTypes.imports -%>

<%- configTypes.imports -%>

<% if (configTypes.imports) {-%>
interface ExtendsAppConfig extends DefaultAppConfig {
<% if (configTypes.imports) { %>
<%- configTypes.exports %>
<% } %>
};

export type AppConfig = ExtendsAppConfig;
<% if (routeConfigTypes.imports) {-%>
type ExtendsRouteConfig = <% if (routeConfigTypes.imports) { %><%- routeConfigTypes.exportNames.join(' & ') %><% } %>;
<% } else { -%>
export type AppConfig = DefaultAppConfig;
type ExtendsRouteConfig = {};
<% } -%>
type RouteConfig = DefaultRouteConfig<ExtendsRouteConfig>;

export {
export type {
AppConfig,
GetAppData,
AppData
}
RouteConfig,
};
6 changes: 6 additions & 0 deletions packages/plugin-auth/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,12 @@ const plugin: Plugin = () => ({
specifier: ['withAuth', 'useAuth'],
source: '@ice/plugin-auth/runtime/Auth',
});

generator.addRouteTypes({
specifier: ['ConfigAuth'],
type: true,
source: '@ice/plugin-auth/esm/types',
});
},
runtime: `${PLUGIN_NAME}/esm/runtime`,
});
Expand Down
4 changes: 4 additions & 0 deletions packages/plugin-auth/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,7 @@ export type Auth = (data?: any) => Promise<AuthConfig> | AuthConfig;
export function defineAuthConfig(fn: Auth) {
return fn;
}

export interface ConfigAuth {
auth?: string[];
}
7 changes: 6 additions & 1 deletion packages/plugin-pha/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ function getDevPath(url: string): string {

const plugin: Plugin<PluginOptions> = (options) => ({
name: '@ice/plugin-pha',
setup: ({ onGetConfig, onHook, context, serverCompileTask }) => {
setup: ({ onGetConfig, onHook, context, serverCompileTask, generator }) => {
ClarkXia marked this conversation as resolved.
Show resolved Hide resolved
const { template } = options || {};
const { command, rootDir } = context;

Expand All @@ -36,6 +36,11 @@ const plugin: Plugin<PluginOptions> = (options) => ({
let getAppConfig: GetAppConfig;
let getRoutesConfig: GetRoutesConfig;

generator.addRouteTypes({
specifier: ['PageConfig'],
type: true,
source: '@ice/plugin-pha/esm/types',
});
// Get server compiler by hooks
onHook(`before.${command as 'start' | 'build'}.run`, async ({ serverCompiler, taskConfigs, urls, ...restAPI }) => {
const taskConfig = taskConfigs.find(({ name }) => name === 'web').config;
Expand Down
7 changes: 3 additions & 4 deletions packages/runtime/src/RouteContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,16 @@ import type { RouteData, RouteConfig } from '@ice/types';
const DataContext = React.createContext<RouteData | undefined>(undefined);
DataContext.displayName = 'Data';

function useData(): RouteData {
function useData<T = any>(): T {
const value = React.useContext(DataContext);
return value;
}
const DataProvider = DataContext.Provider;

const ConfigContext = React.createContext<RouteConfig | undefined>(undefined);
const ConfigContext = React.createContext<RouteConfig<any> | undefined>(undefined);
ConfigContext.displayName = 'Config';

function useConfig(): RouteConfig {
function useConfig<T = {}>(): RouteConfig<T> {
const value = React.useContext(ConfigContext);
return value;
}
Expand All @@ -22,7 +22,6 @@ const ConfigProvider = ConfigContext.Provider;
export {
useData,
DataProvider,

useConfig,
ConfigProvider,
};
2 changes: 1 addition & 1 deletion packages/runtime/src/routesConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ const DOMAttributeNames: Record<string, string> = {
noModule: 'noModule',
};

type ElementProps = RouteConfig['meta'] | RouteConfig['links'] | RouteConfig['scripts'];
type ElementProps = RouteConfig['meta'][0] | RouteConfig['links'][0] | RouteConfig['scripts'][0];

/**
* map element props to dom
Expand Down
7 changes: 6 additions & 1 deletion packages/types/src/generator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,12 @@ export type SetPlugins = (plugins: any) => void;
export type AddExport = (registerKey: string, exportData: ExportData | ExportData[]) => void;
export type RemoveExport = (registerKey: string, removeSource: string | string[]) => void;
export type AddContent = (apiName: string, ...args: any) => void;
export type GetExportStr = (registerKey: string, dataKeys: string[]) => { [x: string]: string };
export type GetExportData = (registerKey: string, dataKeys: string[]) => {
imports?: string;
exports?: string;
exportNames?: string[];
[x: string]: any;
};
export type ParseRenderData = () => Record<string, unknown>;
export type Render = () => void;
export type ModifyRenderData = (registration: RenderDataRegistration) => void;
Expand Down
1 change: 1 addition & 0 deletions packages/types/src/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ export interface ExtendsPluginAPI {
generator: {
addExport: AddExport;
addExportTypes: AddExport;
addRouteTypes: AddExport;
addRenderFile: AddRenderFile;
addRenderTemplate: AddTemplateFiles;
modifyRenderData: ModifyRenderData;
Expand Down
27 changes: 12 additions & 15 deletions packages/types/src/runtime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ import type { ComponentType, ReactNode, PropsWithChildren } from 'react';
import type { HydrationOptions } from 'react-dom/client';
import type { Navigator, Params } from 'react-router-dom';

type useConfig = () => RouteConfig;
type useData = () => RouteData;
type useAppContext = () => AppContext;
type UseConfig = () => RouteConfig<Record<string, any>>;
type UseData = () => RouteData;
type UseAppContext = () => AppContext;

type VoidFunction = () => void;
type AppLifecycle = 'onShow' | 'onHide' | 'onPageNotFound' | 'onShareAppMessage' | 'onUnhandledRejection' | 'onLaunch' | 'onError' | 'onTabItemClick';
Expand All @@ -20,16 +20,13 @@ export type AppData = any;
export type RouteData = any;

// route.getConfig return value
export interface RouteConfig {
export type RouteConfig<T = {}> = T & {
// Support for extends config.
title?: string;
// TODO: fix type
meta?: any[];
links?: any[];
scripts?: any[];

// plugin extends
auth?: string[];
}
meta?: React.MetaHTMLAttributes<HTMLMetaElement>[];
links?: React.LinkHTMLAttributes<HTMLLinkElement>[];
scripts?: React.ScriptHTMLAttributes<HTMLScriptElement>[];
};

export interface AppExport {
default?: AppConfig;
Expand Down Expand Up @@ -167,9 +164,9 @@ export interface RuntimeAPI {
setRender: SetRender;
addWrapper: AddWrapper;
appContext: AppContext;
useData: useData;
useConfig: useConfig;
useAppContext: useAppContext;
useData: UseData;
useConfig: UseConfig;
useAppContext: UseAppContext;
}

export interface RuntimePlugin {
Expand Down