feat: add a new built-in solution icejs3 which is corresponding to the latest icejs framework

This commit is contained in:
LeoYuan 袁力皓 2023-03-29 10:22:17 +08:00 committed by eternalsky
parent a6e768f8e9
commit b607b246d6
304 changed files with 19427 additions and 3 deletions

View File

@ -1,6 +1,6 @@
{
"name": "@alilc/lowcode-code-generator",
"version": "1.0.8",
"version": "1.1.0",
"description": "出码引擎 for LowCode Engine",
"license": "MIT",
"main": "lib/index.js",

View File

@ -9,6 +9,7 @@ import { createModuleBuilder } from './generator/ModuleBuilder';
import { createDiskPublisher } from './publisher/disk';
import { createZipPublisher } from './publisher/zip';
import createIceJsProjectBuilder, { plugins as reactPlugins } from './solutions/icejs';
import createIce3JsProjectBuilder, { plugins as icejs3Plugins } from './solutions/icejs3';
import createRaxAppProjectBuilder, { plugins as raxPlugins } from './solutions/rax-app';
// 引入说明
@ -32,6 +33,7 @@ import * as CONSTANTS from './const';
// 引入内置解决方案模块
import icejs from './plugins/project/framework/icejs';
import icejs3 from './plugins/project/framework/icejs3';
import rax from './plugins/project/framework/rax';
export default {
@ -39,10 +41,12 @@ export default {
createModuleBuilder,
solutions: {
icejs: createIceJsProjectBuilder,
icejs3: createIce3JsProjectBuilder,
rax: createRaxAppProjectBuilder,
},
solutionParts: {
icejs,
icejs3,
rax,
},
publishers: {
@ -74,6 +78,9 @@ export default {
i18n,
utils,
},
icejs3: {
...icejs3Plugins,
},
},
postprocessor: {
prettier,

View File

@ -16,7 +16,7 @@ import {
IWithDependency,
} from '../../types';
import { isValidIdentifier, isValidComponentName } from '../../utils/validate';
import { isValidIdentifier } from '../../utils/validate';
// TODO: main 这个信息到底怎么用,是不是外部包不需要使用?
const DEP_MAIN_BLOCKLIST = ['lib', 'lib/index', 'es', 'es/index', 'main'];
@ -261,7 +261,7 @@ function buildPackageImport(
if (!isValidIdentifier(name)) {
throw new CodeGeneratorError(`Invalid Identifier [${name}]`);
}
if (info.nodeIdentifier && !isValidComponentName(info.nodeIdentifier)) {
if (info.nodeIdentifier && !isValidIdentifier(info.nodeIdentifier)) {
throw new CodeGeneratorError(`Invalid Identifier [${info.nodeIdentifier}]`);
}
});

View File

@ -0,0 +1,17 @@
import template from './template';
import globalStyle from './plugins/globalStyle';
import packageJSON from './plugins/packageJSON';
import layout from './plugins/layout';
import appConfig from './plugins/appConfig';
import buildConfig from './plugins/buildConfig';
export default {
template,
plugins: {
appConfig,
buildConfig,
globalStyle,
packageJSON,
layout,
},
};

View File

@ -0,0 +1,50 @@
import {
BuilderComponentPlugin,
BuilderComponentPluginFactory,
ChunkType,
FileType,
ICodeStruct,
} from '../../../../../types';
import { COMMON_CHUNK_NAME } from '../../../../../const/generator';
export interface AppConfigPluginConfig {
}
function getContent() {
return `import { defineAppConfig } from 'ice';
// App config, see https://v3.ice.work/docs/guide/basic/app
export default defineAppConfig(() => ({
// Set your configs here.
app: {
rootId: 'App',
},
router: {
type: 'browser',
basename: '/',
},
}));`;
}
const pluginFactory: BuilderComponentPluginFactory<AppConfigPluginConfig> = () => {
const plugin: BuilderComponentPlugin = async (pre: ICodeStruct) => {
const next: ICodeStruct = {
...pre,
};
next.chunks.push({
type: ChunkType.STRING,
fileType: FileType.TS,
name: COMMON_CHUNK_NAME.FileMainContent,
content: getContent(),
linkAfter: [],
});
return next;
};
return plugin;
};
export default pluginFactory;

View File

@ -0,0 +1,165 @@
import {
BuilderComponentPlugin,
BuilderComponentPluginFactory,
ChunkType,
FileType,
ICodeStruct,
} from '../../../../../types';
import { COMMON_CHUNK_NAME } from '../../../../../const/generator';
import { format } from '../../../../../utils/format';
import { getThemeInfo } from '../../../../../utils/theme';
export interface BuildConfigPluginConfig {
/** 包名 */
themePackage?: string;
}
function getContent(cfg?: BuildConfigPluginConfig, routesContent?: string) {
return `
import { join } from 'path';
import { defineConfig } from '@ice/app';
import _ from 'lodash';
import fusion from '@ice/plugin-fusion';
import locales from '@ice/plugin-moment-locales';
import type { Plugin } from '@ice/app/esm/types';
interface PluginOptions {
id: string;
}
const plugin: Plugin<PluginOptions> = (options) => ({
// name 可选,插件名称
name: 'plugin-name',
// setup 必选,用于定制工程构建配置
setup: ({ onGetConfig, modifyUserConfig }) => {
modifyUserConfig('codeSplitting', 'page');
onGetConfig((config) => {
config.entry = {
web: join(process.cwd(), '.ice/entry.client.tsx'),
};
config.cssFilename = '[name].css';
config.configureWebpack = config.configureWebpack || [];
config.configureWebpack?.push((webpackConfig) => {
if (webpackConfig.output) {
webpackConfig.output.filename = '[name].js';
webpackConfig.output.chunkFilename = '[name].js';
}
return webpackConfig;
});
config.swcOptions = _.merge(config.swcOptions, {
compilationConfig: {
jsc: {
transform: {
react: {
runtime: 'classic',
},
},
},
}
});
// 解决 webpack publicPath 问题
config.transforms = config.transforms || [];
config.transforms.push((source: string, id: string) => {
if (id.includes('.ice/entry.client.tsx')) {
let code = \`
if (!__webpack_public_path__?.startsWith('http') && document.currentScript) {
// @ts-ignore
__webpack_public_path__ = document.currentScript.src.replace(/^(.*\\\\/)[^/]+$/, '$1');
window.__ICE_ASSETS_MANIFEST__ = window.__ICE_ASSETS_MANIFEST__ || {};
window.__ICE_ASSETS_MANIFEST__.publicPath = __webpack_public_path__;
}
\`;
code += source;
return { code };
}
});
});
},
});
// The project config, see https://v3.ice.work/docs/guide/basic/config
const minify = process.env.NODE_ENV === 'production' ? 'swc' : false;
export default defineConfig(() => ({
ssr: false,
ssg: false,
minify,
${routesContent}
externals: {
react: 'React',
'react-dom': 'ReactDOM',
'react-dom/client': 'ReactDOM',
'@alifd/next': 'Next',
lodash: 'var window._',
'@alilc/lowcode-engine': 'var window.AliLowCodeEngine',
},
plugins: [
fusion(${cfg?.themePackage ? `{
importStyle: 'sass',
themePackage: '${getThemeInfo(cfg.themePackage).name}',
}` : `{
importStyle: true,
}`}),
locales(),
plugin(),
]
}));
`;
}
function getRoutesContent(navData: any, needShell = true) {
const routes = [
'routes: {',
' defineRoutes: route => {',
];
function _getRoutes(nav: any, _routes: string[] = []) {
const { slug, children } = nav;
if (children && children.length > 0) {
children.forEach((_nav: any) => _getRoutes(_nav, _routes));
} else if (slug) {
_routes.push(`route('/${slug}', '${slug}/index.jsx');`);
}
}
if (needShell) {
routes.push(" route('/', 'layout.jsx', () => {");
}
navData?.forEach((nav: any) => {
_getRoutes(nav, routes);
});
if (needShell) {
routes.push(' });');
}
routes.push(' }'); // end of defineRoutes
routes.push(' },'); // end of routes
return routes.join('\n');
}
const pluginFactory: BuilderComponentPluginFactory<BuildConfigPluginConfig> = (cfg?) => {
const plugin: BuilderComponentPlugin = async (pre: ICodeStruct) => {
const next: ICodeStruct = {
...pre,
};
const { navConfig } = next.contextData;
const routesContent = navConfig?.data ? getRoutesContent(navConfig.data, true) : '';
next.chunks.push({
type: ChunkType.STRING,
fileType: FileType.MTS,
name: COMMON_CHUNK_NAME.FileMainContent,
content: format(getContent(cfg, routesContent)),
linkAfter: [],
});
return next;
};
return plugin;
};
export default pluginFactory;

View File

@ -0,0 +1,56 @@
import { COMMON_CHUNK_NAME } from '../../../../../const/generator';
import {
BuilderComponentPlugin,
BuilderComponentPluginFactory,
ChunkType,
FileType,
ICodeStruct,
IProjectInfo,
} from '../../../../../types';
const pluginFactory: BuilderComponentPluginFactory<unknown> = () => {
const plugin: BuilderComponentPlugin = async (pre: ICodeStruct) => {
const next: ICodeStruct = {
...pre,
};
const ir = next.ir as IProjectInfo;
next.chunks.push({
type: ChunkType.STRING,
fileType: FileType.SCSS,
name: COMMON_CHUNK_NAME.StyleDepsImport,
content: `
// 引入默认全局样式
@import '@alifd/next/reset.scss';
`,
linkAfter: [],
});
next.chunks.push({
type: ChunkType.STRING,
fileType: FileType.SCSS,
name: COMMON_CHUNK_NAME.StyleCssContent,
content: `
body {
-webkit-font-smoothing: antialiased;
}
`,
linkAfter: [COMMON_CHUNK_NAME.StyleDepsImport],
});
next.chunks.push({
type: ChunkType.STRING,
fileType: FileType.SCSS,
name: COMMON_CHUNK_NAME.StyleCssContent,
content: ir.css || '',
linkAfter: [COMMON_CHUNK_NAME.StyleDepsImport],
});
return next;
};
return plugin;
};
export default pluginFactory;

View File

@ -0,0 +1,41 @@
import {
BuilderComponentPlugin,
BuilderComponentPluginFactory,
ChunkType,
FileType,
ICodeStruct,
} from '../../../../../types';
import { COMMON_CHUNK_NAME } from '../../../../../const/generator';
const pluginFactory: BuilderComponentPluginFactory<unknown> = () => {
const plugin: BuilderComponentPlugin = async (pre: ICodeStruct) => {
const next: ICodeStruct = {
...pre,
};
next.chunks.push({
type: ChunkType.STRING,
fileType: FileType.JSX,
name: COMMON_CHUNK_NAME.FileMainContent,
content: `
import { Outlet } from 'ice';
import BasicLayout from '@/layouts/BasicLayout';
export default function Layout() {
return (
<BasicLayout>
<Outlet />
</BasicLayout>
);;
}
`,
linkAfter: [],
});
return next;
};
return plugin;
};
export default pluginFactory;

View File

@ -0,0 +1,119 @@
import { PackageJSON } from '@alilc/lowcode-types';
import { COMMON_CHUNK_NAME } from '../../../../../const/generator';
import {
BuilderComponentPlugin,
BuilderComponentPluginFactory,
ChunkType,
FileType,
ICodeStruct,
IProjectInfo,
} from '../../../../../types';
import { buildDataSourceDependencies } from '../../../../../utils/dataSource';
interface IIceJs3PackageJSON extends PackageJSON {
originTemplate: string;
}
export type IceJs3PackageJsonPluginConfig = {
/**
*
*/
datasourceConfig?: {
/** 数据源引擎的版本 */
engineVersion?: string;
/** 数据源引擎的包名 */
enginePackage?: string;
/** 数据源 handlers 的版本 */
handlersVersion?: {
[key: string]: string;
};
/** 数据源 handlers 的包名 */
handlersPackages?: {
[key: string]: string;
};
};
/** 包名 */
packageName?: string;
/** 版本 */
packageVersion?: string;
};
const pluginFactory: BuilderComponentPluginFactory<IceJs3PackageJsonPluginConfig> = (cfg) => {
const plugin: BuilderComponentPlugin = async (pre: ICodeStruct) => {
const next: ICodeStruct = {
...pre,
};
const ir = next.ir as IProjectInfo;
const packageJson: IIceJs3PackageJSON = {
name: cfg?.packageName || 'icejs3-demo-app',
version: cfg?.packageVersion || '0.1.5',
description: 'icejs 3 轻量级模板,使用 JavaScript仅包含基础的 Layout。',
dependencies: {
moment: '^2.24.0',
react: '^18.2.0',
'react-dom': '^18.2.0',
'react-router': '^6.9.0',
'react-router-dom': '^6.9.0',
'intl-messageformat': '^9.3.6',
'@alifd/next': '1.26.15',
'@ice/runtime': '^1.0.0',
// 数据源相关的依赖:
...buildDataSourceDependencies(ir, cfg?.datasourceConfig),
},
devDependencies: {
'@ice/app': '^3.0.0',
'@types/react': '^18.0.0',
'@types/react-dom': '^18.0.0',
'@types/node': '^18.11.17',
'@ice/plugin-fusion': '^1.0.1',
'@ice/plugin-moment-locales': '^1.0.0',
eslint: '^6.0.1',
stylelint: '^13.2.0',
},
scripts: {
start: 'ice start',
build: 'ice build',
lint: 'npm run eslint && npm run stylelint',
eslint: 'eslint --cache --ext .js,.jsx ./',
stylelint: 'stylelint ./**/*.scss',
},
engines: {
node: '>=14.0.0',
},
repository: {
type: 'git',
url: 'http://gitlab.xxx.com/msd/leak-scan/tree/master',
},
private: true,
originTemplate: '@alifd/scaffold-lite-js',
};
ir.packages.forEach((packageInfo) => {
packageJson.dependencies[packageInfo.package] = packageInfo.version;
});
next.chunks.push({
type: ChunkType.JSON,
fileType: FileType.JSON,
name: COMMON_CHUNK_NAME.FileMainContent,
content: packageJson,
linkAfter: [],
});
return next;
};
return plugin;
};
export default pluginFactory;

View File

@ -0,0 +1,12 @@
import { ResultFile } from '@alilc/lowcode-types';
import { createResultFile } from '../../../../../../utils/resultHelper';
export default function getFile(): [string[], ResultFile] {
const file = createResultFile(
'README',
'md',
'This project is generated by lowcode-code-generator & lowcode-solution-icejs3.',
);
return [[], file];
}

View File

@ -0,0 +1,14 @@
import { ResultFile } from '@alilc/lowcode-types';
import { createResultFile } from '../../../../../../utils/resultHelper';
export default function getFile(): [string[], ResultFile] {
const file = createResultFile(
'.browserslistrc',
'',
`defaults
ios_saf 9
`,
);
return [[], file];
}

View File

@ -0,0 +1,41 @@
import { ResultFile } from '@alilc/lowcode-types';
import { createResultFile } from '../../../../../../utils/resultHelper';
/* eslint-disable max-len */
export default function getFile(): [string[], ResultFile] {
const file = createResultFile(
'document',
'tsx',
`import React from 'react';
import { Meta, Title, Links, Main, Scripts } from 'ice';
export default function Document() {
return (
<html>
<head>
<meta charSet="utf-8" />
<meta name="description" content="ice.js 3 lite scaffold" />
<link rel="icon" href="/favicon.ico" />
<link rel="stylesheet" href="//alifd.alicdn.com/npm/@alifd/next/1.21.16/next.min.css" />
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" />
<Meta />
<Title />
<Links />
</head>
<body>
<Main />
<script crossOrigin="anonymous" src="//g.alicdn.com/code/lib/react/18.2.0/umd/react.development.js" />
<script crossOrigin="anonymous" src="//g.alicdn.com/code/lib/react-dom/18.2.0/umd/react-dom.development.js" />
<script crossOrigin="anonymous" src="//g.alicdn.com/code/lib/??react-router/6.9.0/react-router.production.min.js,react-router-dom/6.9.0/react-router-dom.production.min.js" />
<script crossOrigin="anonymous" src="//alifd.alicdn.com/npm/@alifd/next/1.26.15/next.min.js" />
<script crossOrigin="anonymous" src="//g.alicdn.com/code/lib/prop-types/15.7.2/prop-types.js" />
<script crossOrigin="anonymous" src="//g.alicdn.com/platform/c/??lodash/4.6.1/lodash.min.js,immutable/3.7.6/dist/immutable.min.js" />
<Scripts />
</body>
</html>
);
}`,
);
return [['src'], file];
}

View File

@ -0,0 +1,36 @@
import { ResultFile } from '@alilc/lowcode-types';
import { createResultFile } from '../../../../../../utils/resultHelper';
export default function getFile(): [string[], ResultFile] {
const file = createResultFile(
'.gitignore',
'',
`
# See https://help.github.com/ignore-files/ for more about ignoring files.
# dependencies
node_modules/
# production
build/
dist/
tmp/
lib/
# misc
.idea/
.happypack
.DS_Store
*.swp
*.dia~
.ice
npm-debug.log*
yarn-debug.log*
yarn-error.log*
index.module.scss.d.ts
`,
);
return [[], file];
}

View File

@ -0,0 +1,25 @@
import { ResultFile } from '@alilc/lowcode-types';
import { createResultFile } from '../../../../../../../../../../../utils/resultHelper';
export default function getFile(): [string[], ResultFile] {
const file = createResultFile(
'index',
'jsx',
`
import React from 'react';
import styles from './index.module.scss';
export default function Footer() {
return (
<p className={styles.footer}>
<span className={styles.logo}>Alibaba Fusion</span>
<br />
<span className={styles.copyright}>© 2019- Alibaba Fusion & ICE</span>
</p>
);
}
`,
);
return [['src', 'layouts', 'BasicLayout', 'components', 'Footer'], file];
}

View File

@ -0,0 +1,26 @@
import { ResultFile } from '@alilc/lowcode-types';
import { createResultFile } from '../../../../../../../../../../../utils/resultHelper';
export default function getFile(): [string[], ResultFile] {
const file = createResultFile(
'index',
'module.scss',
`
.footer {
line-height: 20px;
text-align: center;
}
.logo {
font-weight: bold;
font-size: 16px;
}
.copyright {
font-size: 12px;
}
`,
);
return [['src', 'layouts', 'BasicLayout', 'components', 'Footer'], file];
}

View File

@ -0,0 +1,27 @@
import { ResultFile } from '@alilc/lowcode-types';
import { createResultFile } from '../../../../../../../../../../../utils/resultHelper';
export default function getFile(): [string[], ResultFile] {
const file = createResultFile(
'index',
'jsx',
`
import React from 'react';
import { Link } from 'ice';
import styles from './index.module.scss';
export default function Logo({ image, text, url }) {
return (
<div className="logo">
<Link to={url || '/'} className={styles.logo}>
{image && <img src={image} alt="logo" />}
<span>{text}</span>
</Link>
</div>
);
}
`,
);
return [['src', 'layouts', 'BasicLayout', 'components', 'Logo'], file];
}

View File

@ -0,0 +1,31 @@
import { ResultFile } from '@alilc/lowcode-types';
import { createResultFile } from '../../../../../../../../../../../utils/resultHelper';
export default function getFile(): [string[], ResultFile] {
const file = createResultFile(
'index',
'module.scss',
`
.logo{
display: flex;
align-items: center;
justify-content: center;
color: $color-text1-1;
font-weight: bold;
font-size: 14px;
line-height: 22px;
&:visited, &:link {
color: $color-text1-1;
}
img {
height: 24px;
margin-right: 10px;
}
}
`,
);
return [['src', 'layouts', 'BasicLayout', 'components', 'Logo'], file];
}

View File

@ -0,0 +1,79 @@
import { ResultFile } from '@alilc/lowcode-types';
import { createResultFile } from '../../../../../../../../../../../utils/resultHelper';
export default function getFile(): [string[], ResultFile] {
const file = createResultFile(
'index',
'jsx',
`import React from 'react';
import PropTypes from 'prop-types';
import { Link, useLocation } from 'ice';
import { Nav } from '@alifd/next';
import { asideMenuConfig } from '../../menuConfig';
const { SubNav } = Nav;
const NavItem = Nav.Item;
function getNavMenuItems(menusData) {
if (!menusData) {
return [];
}
return menusData
.filter(item => item.name && !item.hideInMenu)
.map((item, index) => getSubMenuOrItem(item, index));
}
function getSubMenuOrItem(item, index) {
if (item.children && item.children.some(child => child.name)) {
const childrenItems = getNavMenuItems(item.children);
if (childrenItems && childrenItems.length > 0) {
const subNav = (
<SubNav key={index} icon={item.icon} label={item.name}>
{childrenItems}
</SubNav>
);
return subNav;
}
return null;
}
const navItem = (
<NavItem key={item.path} icon={item.icon}>
<Link to={item.path}>{item.name}</Link>
</NavItem>
);
return navItem;
}
const Navigation = (props, context) => {
const location = useLocation();
const { pathname } = location;
const { isCollapse } = context;
return (
<Nav
type="primary"
selectedKeys={[pathname]}
defaultSelectedKeys={[pathname]}
embeddable
openMode="single"
iconOnly={isCollapse}
hasArrow={false}
mode={isCollapse ? 'popup' : 'inline'}
>
{getNavMenuItems(asideMenuConfig)}
</Nav>
);
};
Navigation.contextTypes = {
isCollapse: PropTypes.bool,
};
export default Navigation;
`,
);
return [['src', 'layouts', 'BasicLayout', 'components', 'PageNav'], file];
}

View File

@ -0,0 +1,92 @@
import { ResultFile } from '@alilc/lowcode-types';
import { createResultFile } from '../../../../../../../../../utils/resultHelper';
export default function getFile(): [string[], ResultFile] {
const file = createResultFile(
'index',
'jsx',
`
import React, { useState } from 'react';
import { Shell, ConfigProvider } from '@alifd/next';
import PageNav from './components/PageNav';
import Logo from './components/Logo';
import Footer from './components/Footer';
(function() {
const throttle = function(type, name, obj = window) {
let running = false;
const func = () => {
if (running) {
return;
}
running = true;
requestAnimationFrame(() => {
obj.dispatchEvent(new CustomEvent(name));
running = false;
});
};
obj.addEventListener(type, func);
};
throttle('resize', 'optimizedResize');
})();
export default function BasicLayout({ children }) {
const getDevice = width => {
const isPhone =
typeof navigator !== 'undefined' && navigator && navigator.userAgent.match(/phone/gi);
if (width < 680 || isPhone) {
return 'phone';
}
if (width < 1280 && width > 680) {
return 'tablet';
}
return 'desktop';
};
const [device, setDevice] = useState(getDevice(NaN));
window.addEventListener('optimizedResize', e => {
setDevice(getDevice(e && e.target && e.target.innerWidth));
});
return (
<ConfigProvider device={device}>
<Shell
type="dark"
style={{
minHeight: '100vh',
}}
>
<Shell.Branding>
<Logo
image="https://img.alicdn.com/tfs/TB1.ZBecq67gK0jSZFHXXa9jVXa-904-826.png"
text="Logo"
/>
</Shell.Branding>
<Shell.Navigation
direction="hoz"
style={{
marginRight: 10,
}}
></Shell.Navigation>
<Shell.Action></Shell.Action>
<Shell.Navigation>
<PageNav />
</Shell.Navigation>
<Shell.Content>{children}</Shell.Content>
<Shell.Footer>
<Footer />
</Shell.Footer>
</Shell>
</ConfigProvider>
);
}
`,
);
return [['src', 'layouts', 'BasicLayout'], file];
}

View File

@ -0,0 +1,22 @@
import { ResultFile } from '@alilc/lowcode-types';
import { createResultFile } from '../../../../../../../../../utils/resultHelper';
export default function getFile(): [string[], ResultFile] {
const file = createResultFile(
'menuConfig',
'js',
`
const headerMenuConfig = [];
const asideMenuConfig = [
{
name: 'Dashboard',
path: '/',
icon: 'smile',
},
];
export { headerMenuConfig, asideMenuConfig };
`,
);
return [['src', 'layouts', 'BasicLayout'], file];
}

View File

@ -0,0 +1,38 @@
import { ResultFile } from '@alilc/lowcode-types';
import { createResultFile } from '../../../../../../utils/resultHelper';
export default function getFile(): [string[], ResultFile] {
const file = createResultFile(
'tsconfig',
'json',
`
{
"compilerOptions": {
"baseUrl": "./",
"module": "ESNext",
"target": "ESNext",
"lib": ["DOM", "ESNext", "DOM.Iterable"],
"jsx": "react-jsx",
"moduleResolution": "node",
"allowSyntheticDefaultImports": true,
"forceConsistentCasingInFileNames": true,
"noImplicitReturns": true,
"noImplicitThis": true,
"noImplicitAny": false,
"importHelpers": true,
"strictNullChecks": true,
"suppressImplicitAnyIndexErrors": true,
"skipLibCheck": true,
"paths": {
"@/*": ["./src/*"],
"ice": [".ice"]
}
},
"include": ["src", ".ice"],
"exclude": ["build"]
}
`,
);
return [[], file];
}

View File

@ -0,0 +1,20 @@
import { ResultFile } from '@alilc/lowcode-types';
import { createResultFile } from '../../../../../../utils/resultHelper';
export default function getFile(): [string[], ResultFile] {
const file = createResultFile(
'typings.d',
'ts',
`/// <reference types="@ice/app/types" />
export {};
declare global {
interface Window {
g_config: Record<string, any>;
}
}
`,
);
return [['src'], file];
}

View File

@ -0,0 +1,57 @@
import { IProjectTemplate } from '../../../../../types';
import { generateStaticFiles } from './static-files';
const icejs3Template: IProjectTemplate = {
slots: {
components: {
path: ['src', 'components'],
fileName: 'index',
},
pages: {
path: ['src', 'pages'],
fileName: 'index',
},
entry: {
path: ['src'],
fileName: 'app',
},
constants: {
path: ['src'],
fileName: 'constants',
},
utils: {
path: ['src'],
fileName: 'utils',
},
i18n: {
path: ['src'],
fileName: 'i18n',
},
globalStyle: {
path: ['src'],
fileName: 'global',
},
packageJSON: {
path: [],
fileName: 'package',
},
appConfig: {
path: ['src'],
fileName: 'app',
},
buildConfig: {
path: [],
fileName: 'ice.config',
},
layout: {
path: ['src', 'pages'],
fileName: 'layout',
},
},
generateTemplate() {
return generateStaticFiles();
},
};
export default icejs3Template;

View File

@ -0,0 +1,33 @@
import { ResultDir } from '@alilc/lowcode-types';
import { createResultDir } from '../../../../../utils/resultHelper';
import { runFileGenerator } from '../../../../../utils/templateHelper';
import file1 from './files/gitignore';
import file2 from './files/README.md';
import file3 from './files/browserslistrc';
import file4 from './files/typings';
import file5 from './files/document';
import file6 from './files/src/layouts/BasicLayout/components/Footer/index.jsx';
import file7 from './files/src/layouts/BasicLayout/components/Footer/index.style';
import file8 from './files/src/layouts/BasicLayout/components/Logo/index.jsx';
import file9 from './files/src/layouts/BasicLayout/components/Logo/index.style';
import file10 from './files/src/layouts/BasicLayout/components/PageNav/index.jsx';
import file11 from './files/src/layouts/BasicLayout/index.jsx';
import file12 from './files/src/layouts/BasicLayout/menuConfig.js';
export function generateStaticFiles(root = createResultDir('.')): ResultDir {
runFileGenerator(root, file1);
runFileGenerator(root, file2);
runFileGenerator(root, file3);
runFileGenerator(root, file4);
runFileGenerator(root, file5);
runFileGenerator(root, file6);
runFileGenerator(root, file7);
runFileGenerator(root, file8);
runFileGenerator(root, file9);
runFileGenerator(root, file10);
runFileGenerator(root, file11);
runFileGenerator(root, file12);
return root;
}

View File

@ -0,0 +1,109 @@
import { IProjectBuilder, IProjectBuilderOptions } from '../types';
import { createProjectBuilder } from '../generator/ProjectBuilder';
import esmodule from '../plugins/common/esmodule';
import containerClass from '../plugins/component/react/containerClass';
import containerInitState from '../plugins/component/react/containerInitState';
import containerInjectContext from '../plugins/component/react/containerInjectContext';
import containerInjectUtils from '../plugins/component/react/containerInjectUtils';
import containerInjectDataSourceEngine from '../plugins/component/react/containerInjectDataSourceEngine';
import containerInjectConstants from '../plugins/component/react/containerInjectConstants';
import containerInjectI18n from '../plugins/component/react/containerInjectI18n';
import containerLifeCycle from '../plugins/component/react/containerLifeCycle';
import containerMethod from '../plugins/component/react/containerMethod';
import jsx from '../plugins/component/react/jsx';
import reactCommonDeps from '../plugins/component/react/reactCommonDeps';
import css from '../plugins/component/style/css';
import constants from '../plugins/project/constants';
import i18n from '../plugins/project/i18n';
import utils from '../plugins/project/utils';
import icejs3 from '../plugins/project/framework/icejs3';
import { prettier } from '../postprocessor';
export type IceJs3ProjectBuilderOptions = IProjectBuilderOptions;
export default function createIceJsProjectBuilder(
options?: IceJs3ProjectBuilderOptions,
): IProjectBuilder {
return createProjectBuilder({
inStrictMode: options?.inStrictMode,
extraContextData: { ...options },
template: icejs3.template,
plugins: {
components: [
reactCommonDeps(),
esmodule({
fileType: 'jsx',
}),
containerClass(),
containerInjectContext(),
containerInjectUtils(),
containerInjectDataSourceEngine(),
containerInjectI18n(),
containerInitState(),
containerLifeCycle(),
containerMethod(),
jsx({
nodeTypeMapping: {
Div: 'div',
Component: 'div',
Page: 'div',
Block: 'div',
},
}),
css(),
],
pages: [
reactCommonDeps(),
esmodule({
fileType: 'jsx',
}),
containerClass(),
containerInjectContext(),
containerInjectUtils(),
containerInjectDataSourceEngine(),
containerInjectI18n(),
containerInjectConstants(),
containerInitState(),
containerLifeCycle(),
containerMethod(),
jsx({
nodeTypeMapping: {
Div: 'div',
Component: 'div',
Page: 'div',
Block: 'div',
Box: 'div',
},
}),
css(),
],
constants: [constants()],
utils: [esmodule(), utils('react')],
i18n: [i18n()],
globalStyle: [icejs3.plugins.globalStyle()],
packageJSON: [icejs3.plugins.packageJSON()],
buildConfig: [icejs3.plugins.buildConfig()],
appConfig: [icejs3.plugins.appConfig()],
layout: [icejs3.plugins.layout()],
},
postProcessors: [prettier()],
customizeBuilderOptions: options?.customizeBuilderOptions,
});
}
export const plugins = {
containerClass,
containerInitState,
containerInjectContext,
containerInjectUtils,
containerInjectI18n,
containerInjectDataSourceEngine,
containerLifeCycle,
containerMethod,
jsx,
commonDeps: reactCommonDeps,
};

View File

@ -0,0 +1,12 @@
import prettier from 'prettier';
import parserBabel from 'prettier/parser-babel';
export function format(content: string, options = {}) {
return prettier.format(content, {
parser: 'babel',
plugins: [parserBabel],
singleQuote: true,
jsxSingleQuote: false,
...options,
});
}

View File

@ -0,0 +1,20 @@
/**
*
* @param theme theme @alife/theme-97 @alife/theme-97@^1.0.0
*/
export interface ThemeInfo {
name: string;
version?: string;
}
export function getThemeInfo(theme: string): ThemeInfo {
const sepIdx = theme.indexOf('@', 1);
if (sepIdx === -1) {
return { name: theme };
}
return {
name: theme.slice(0, sepIdx),
version: theme.slice(sepIdx + 1),
};
}

View File

@ -0,0 +1,3 @@
defaults
ios_saf 9

View File

@ -0,0 +1,25 @@
# See https://help.github.com/ignore-files/ for more about ignoring files.
# dependencies
node_modules/
# production
build/
dist/
tmp/
lib/
# misc
.idea/
.happypack
.DS_Store
*.swp
*.dia~
.ice
npm-debug.log*
yarn-debug.log*
yarn-error.log*
index.module.scss.d.ts

View File

@ -0,0 +1 @@
This project is generated by lowcode-code-generator & lowcode-solution-icejs3.

View File

@ -0,0 +1,90 @@
import { join } from 'path';
import { defineConfig } from '@ice/app';
import _ from 'lodash';
import fusion from '@ice/plugin-fusion';
import locales from '@ice/plugin-moment-locales';
import type { Plugin } from '@ice/app/esm/types';
interface PluginOptions {
id: string;
}
const plugin: Plugin<PluginOptions> = (options) => ({
// name 可选,插件名称
name: 'plugin-name',
// setup 必选,用于定制工程构建配置
setup: ({ onGetConfig, modifyUserConfig }) => {
modifyUserConfig('codeSplitting', 'page');
onGetConfig((config) => {
config.entry = {
web: join(process.cwd(), '.ice/entry.client.tsx'),
};
config.cssFilename = '[name].css';
config.configureWebpack = config.configureWebpack || [];
config.configureWebpack?.push((webpackConfig) => {
if (webpackConfig.output) {
webpackConfig.output.filename = '[name].js';
webpackConfig.output.chunkFilename = '[name].js';
}
return webpackConfig;
});
config.swcOptions = _.merge(config.swcOptions, {
compilationConfig: {
jsc: {
transform: {
react: {
runtime: 'classic',
},
},
},
},
});
// 解决 webpack publicPath 问题
config.transforms = config.transforms || [];
config.transforms.push((source: string, id: string) => {
if (id.includes('.ice/entry.client.tsx')) {
let code = `
if (!__webpack_public_path__?.startsWith('http') && document.currentScript) {
// @ts-ignore
__webpack_public_path__ = document.currentScript.src.replace(/^(.*\\/)[^/]+$/, '$1');
window.__ICE_ASSETS_MANIFEST__ = window.__ICE_ASSETS_MANIFEST__ || {};
window.__ICE_ASSETS_MANIFEST__.publicPath = __webpack_public_path__;
}
`;
code += source;
return { code };
}
});
});
},
});
// The project config, see https://v3.ice.work/docs/guide/basic/config
const minify = process.env.NODE_ENV === 'production' ? 'swc' : false;
export default defineConfig(() => ({
ssr: false,
ssg: false,
minify,
externals: {
react: 'React',
'react-dom': 'ReactDOM',
'react-dom/client': 'ReactDOM',
'@alifd/next': 'Next',
lodash: 'var window._',
'@alilc/lowcode-engine': 'var window.AliLowCodeEngine',
},
plugins: [
fusion({
importStyle: true,
}),
locales(),
plugin(),
],
}));

View File

@ -0,0 +1,44 @@
{
"name": "icejs3-demo-app",
"version": "0.1.5",
"description": "icejs 3 轻量级模板,使用 JavaScript仅包含基础的 Layout。",
"dependencies": {
"moment": "^2.24.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-router": "^6.9.0",
"react-router-dom": "^6.9.0",
"intl-messageformat": "^9.3.6",
"@alifd/next": "1.19.18",
"@ice/runtime": "^1.0.0",
"@alilc/lowcode-datasource-engine": "^1.0.0",
"@alilc/lowcode-datasource-url-params-handler": "^1.0.0",
"@alilc/lowcode-datasource-fetch-handler": "^1.0.0"
},
"devDependencies": {
"@ice/app": "^3.0.0",
"@types/react": "^18.0.0",
"@types/react-dom": "^18.0.0",
"@types/node": "^18.11.17",
"@ice/plugin-fusion": "^1.0.1",
"@ice/plugin-moment-locales": "^1.0.0",
"eslint": "^6.0.1",
"stylelint": "^13.2.0"
},
"scripts": {
"start": "ice start",
"build": "ice build",
"lint": "npm run eslint && npm run stylelint",
"eslint": "eslint --cache --ext .js,.jsx ./",
"stylelint": "stylelint ./**/*.scss"
},
"engines": {
"node": ">=14.0.0"
},
"repository": {
"type": "git",
"url": "http://gitlab.xxx.com/msd/leak-scan/tree/master"
},
"private": true,
"originTemplate": "@alifd/scaffold-lite-js"
}

View File

@ -0,0 +1,13 @@
import { defineAppConfig } from 'ice';
// App config, see https://v3.ice.work/docs/guide/basic/app
export default defineAppConfig(() => ({
// Set your configs here.
app: {
rootId: 'App',
},
router: {
type: 'browser',
basename: '/',
},
}));

View File

@ -0,0 +1,3 @@
const __$$constants = { ENV: 'prod', DOMAIN: 'xxx.xxx.com' };
export default __$$constants;

View File

@ -0,0 +1,29 @@
import React from 'react';
import { Meta, Title, Links, Main, Scripts } from 'ice';
export default function Document() {
return (
<html>
<head>
<meta charSet="utf-8" />
<meta name="description" content="ice.js 3 lite scaffold" />
<link rel="icon" href="/favicon.ico" />
<link rel="stylesheet" href="//alifd.alicdn.com/npm/@alifd/next/1.21.16/next.min.css" />
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" />
<Meta />
<Title />
<Links />
</head>
<body>
<Main />
<script crossOrigin="anonymous" src="//g.alicdn.com/code/lib/react/18.2.0/umd/react.development.js" />
<script crossOrigin="anonymous" src="//g.alicdn.com/code/lib/react-dom/18.2.0/umd/react-dom.development.js" />
<script crossOrigin="anonymous" src="//g.alicdn.com/code/lib/??react-router/6.9.0/react-router.production.min.js,react-router-dom/6.9.0/react-router-dom.production.min.js" />
<script crossOrigin="anonymous" src="//alifd.alicdn.com/npm/@alifd/next/1.26.15/next.min.js" />
<script crossOrigin="anonymous" src="//g.alicdn.com/code/lib/prop-types/15.7.2/prop-types.js" />
<script crossOrigin="anonymous" src="//g.alicdn.com/platform/c/??lodash/4.6.1/lodash.min.js,immutable/3.7.6/dist/immutable.min.js" />
<Scripts />
</body>
</html>
);
}

View File

@ -0,0 +1,13 @@
// 引入默认全局样式
@import '@alifd/next/reset.scss';
body {
-webkit-font-smoothing: antialiased;
}
body {
font-size: 12px;
}
.table {
width: 100px;
}

View File

@ -0,0 +1,77 @@
const i18nConfig = {};
let locale =
typeof navigator === 'object' && typeof navigator.language === 'string'
? navigator.language
: 'zh-CN';
const getLocale = () => locale;
const setLocale = (target) => {
locale = target;
};
const isEmptyVariables = (variables) =>
(Array.isArray(variables) && variables.length === 0) ||
(typeof variables === 'object' &&
(!variables || Object.keys(variables).length === 0));
// 按低代码规范里面的要求进行变量替换
const format = (msg, variables) =>
typeof msg === 'string'
? msg.replace(/\$\{(\w+)\}/g, (match, key) => variables?.[key] ?? '')
: msg;
const i18nFormat = ({ id, defaultMessage, fallback }, variables) => {
const msg =
i18nConfig[locale]?.[id] ??
i18nConfig[locale.replace('-', '_')]?.[id] ??
defaultMessage;
if (msg == null) {
console.warn('[i18n]: unknown message id: %o (locale=%o)', id, locale);
return fallback === undefined ? `${id}` : fallback;
}
return format(msg, variables);
};
const i18n = (id, params) => {
return i18nFormat({ id }, params);
};
// 将国际化的一些方法注入到目标对象&上下文中
const _inject2 = (target) => {
target.i18n = i18n;
target.getLocale = getLocale;
target.setLocale = (locale) => {
setLocale(locale);
target.forceUpdate();
};
target._i18nText = (t) => {
// 优先取直接传过来的语料
const localMsg = t[locale] ?? t[String(locale).replace('-', '_')];
if (localMsg != null) {
return format(localMsg, t.params);
}
// 其次用项目级别的
const projectMsg = i18nFormat({ id: t.key, fallback: null }, t.params);
if (projectMsg != null) {
return projectMsg;
}
// 兜底用 use 指定的或默认语言的
return format(t[t.use || 'zh-CN'] ?? t.en_US, t.params);
};
// 注入到上下文中去
if (target._context && target._context !== target) {
Object.assign(target._context, {
i18n,
getLocale,
setLocale: target.setLocale,
});
}
};
export { getLocale, setLocale, i18n, i18nFormat, _inject2 };

View File

@ -0,0 +1,14 @@
import React from 'react';
import styles from './index.module.scss';
export default function Footer() {
return (
<p className={styles.footer}>
<span className={styles.logo}>Alibaba Fusion</span>
<br />
<span className={styles.copyright}>© 2019-现在 Alibaba Fusion & ICE</span>
</p>
);
}

View File

@ -0,0 +1,15 @@
.footer {
line-height: 20px;
text-align: center;
}
.logo {
font-weight: bold;
font-size: 16px;
}
.copyright {
font-size: 12px;
}

View File

@ -0,0 +1,16 @@
import React from 'react';
import { Link } from 'ice';
import styles from './index.module.scss';
export default function Logo({ image, text, url }) {
return (
<div className="logo">
<Link to={url || '/'} className={styles.logo}>
{image && <img src={image} alt="logo" />}
<span>{text}</span>
</Link>
</div>
);
}

View File

@ -0,0 +1,20 @@
.logo{
display: flex;
align-items: center;
justify-content: center;
color: $color-text1-1;
font-weight: bold;
font-size: 14px;
line-height: 22px;
&:visited, &:link {
color: $color-text1-1;
}
img {
height: 24px;
margin-right: 10px;
}
}

View File

@ -0,0 +1,68 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Link, useLocation } from 'ice';
import { Nav } from '@alifd/next';
import { asideMenuConfig } from '../../menuConfig';
const { SubNav } = Nav;
const NavItem = Nav.Item;
function getNavMenuItems(menusData) {
if (!menusData) {
return [];
}
return menusData
.filter(item => item.name && !item.hideInMenu)
.map((item, index) => getSubMenuOrItem(item, index));
}
function getSubMenuOrItem(item, index) {
if (item.children && item.children.some(child => child.name)) {
const childrenItems = getNavMenuItems(item.children);
if (childrenItems && childrenItems.length > 0) {
const subNav = (
<SubNav key={index} icon={item.icon} label={item.name}>
{childrenItems}
</SubNav>
);
return subNav;
}
return null;
}
const navItem = (
<NavItem key={item.path} icon={item.icon}>
<Link to={item.path}>{item.name}</Link>
</NavItem>
);
return navItem;
}
const Navigation = (props, context) => {
const location = useLocation();
const { pathname } = location;
const { isCollapse } = context;
return (
<Nav
type="primary"
selectedKeys={[pathname]}
defaultSelectedKeys={[pathname]}
embeddable
openMode="single"
iconOnly={isCollapse}
hasArrow={false}
mode={isCollapse ? 'popup' : 'inline'}
>
{getNavMenuItems(asideMenuConfig)}
</Nav>
);
};
Navigation.contextTypes = {
isCollapse: PropTypes.bool,
};
export default Navigation;

View File

@ -0,0 +1,81 @@
import React, { useState } from 'react';
import { Shell, ConfigProvider } from '@alifd/next';
import PageNav from './components/PageNav';
import Logo from './components/Logo';
import Footer from './components/Footer';
(function() {
const throttle = function(type, name, obj = window) {
let running = false;
const func = () => {
if (running) {
return;
}
running = true;
requestAnimationFrame(() => {
obj.dispatchEvent(new CustomEvent(name));
running = false;
});
};
obj.addEventListener(type, func);
};
throttle('resize', 'optimizedResize');
})();
export default function BasicLayout({ children }) {
const getDevice = width => {
const isPhone =
typeof navigator !== 'undefined' && navigator && navigator.userAgent.match(/phone/gi);
if (width < 680 || isPhone) {
return 'phone';
}
if (width < 1280 && width > 680) {
return 'tablet';
}
return 'desktop';
};
const [device, setDevice] = useState(getDevice(NaN));
window.addEventListener('optimizedResize', e => {
setDevice(getDevice(e && e.target && e.target.innerWidth));
});
return (
<ConfigProvider device={device}>
<Shell
type="dark"
style={{
minHeight: '100vh',
}}
>
<Shell.Branding>
<Logo
image="https://img.alicdn.com/tfs/TB1.ZBecq67gK0jSZFHXXa9jVXa-904-826.png"
text="Logo"
/>
</Shell.Branding>
<Shell.Navigation
direction="hoz"
style={{
marginRight: 10,
}}
></Shell.Navigation>
<Shell.Action></Shell.Action>
<Shell.Navigation>
<PageNav />
</Shell.Navigation>
<Shell.Content>{children}</Shell.Content>
<Shell.Footer>
<Footer />
</Shell.Footer>
</Shell>
</ConfigProvider>
);
}

View File

@ -0,0 +1,11 @@
const headerMenuConfig = [];
const asideMenuConfig = [
{
name: 'Dashboard',
path: '/',
icon: 'smile',
},
];
export { headerMenuConfig, asideMenuConfig };

View File

@ -0,0 +1,195 @@
// : "__$$" 访
// react
import React from 'react';
import { Form, Input, NumberPicker, Select, Button } from '@alifd/next';
import { createUrlParamsHandler as __$$createUrlParamsRequestHandler } from '@alilc/lowcode-datasource-url-params-handler';
import { createFetchHandler as __$$createFetchRequestHandler } from '@alilc/lowcode-datasource-fetch-handler';
import { create as __$$createDataSourceEngine } from '@alilc/lowcode-datasource-engine/runtime';
import utils, { RefsManager } from '../../utils';
import * as __$$i18n from '../../i18n';
import __$$constants from '../../constants';
import './index.css';
class Test$$Page extends React.Component {
_context = this;
_dataSourceConfig = this._defineDataSourceConfig();
_dataSourceEngine = __$$createDataSourceEngine(this._dataSourceConfig, this, {
runtimeConfig: true,
requestHandlersMap: {
urlParams: __$$createUrlParamsRequestHandler(window.location.search),
fetch: __$$createFetchRequestHandler(),
},
});
get dataSourceMap() {
return this._dataSourceEngine.dataSourceMap || {};
}
reloadDataSource = async () => {
await this._dataSourceEngine.reloadDataSource();
};
get constants() {
return __$$constants || {};
}
constructor(props, context) {
super(props);
this.utils = utils;
this._refsManager = new RefsManager();
__$$i18n._inject2(this);
this.state = { text: 'outter' };
}
$ = (refName) => {
return this._refsManager.get(refName);
};
$$ = (refName) => {
return this._refsManager.getAll(refName);
};
_defineDataSourceConfig() {
const _this = this;
return {
list: [
{
id: 'urlParams',
type: 'urlParams',
isInit: function () {
return undefined;
}.bind(_this),
options: function () {
return undefined;
}.bind(_this),
},
{
id: 'user',
type: 'fetch',
options: function () {
return {
method: 'GET',
uri: 'https://shs.xxx.com/mock/1458/demo/user',
isSync: true,
};
}.bind(_this),
dataHandler: function (response) {
if (!response.data.success) {
throw new Error(response.data.message);
}
return response.data.data;
},
isInit: function () {
return undefined;
}.bind(_this),
},
{
id: 'orders',
type: 'fetch',
options: function () {
return {
method: 'GET',
uri: 'https://shs.xxx.com/mock/1458/demo/orders',
isSync: true,
};
}.bind(_this),
dataHandler: function (response) {
if (!response.data.success) {
throw new Error(response.data.message);
}
return response.data.data.result;
},
isInit: function () {
return undefined;
}.bind(_this),
},
],
dataHandler: function (dataMap) {
console.info('All datasources loaded:', dataMap);
},
};
}
componentDidMount() {
this._dataSourceEngine.reloadDataSource();
console.log('componentDidMount');
}
render() {
const __$$context = this._context || this;
const { state } = __$$context;
return (
<div ref={this._refsManager.linkRef('outterView')} autoLoading={true}>
<Form
labelCol={__$$eval(() => this.state.colNum)}
style={{}}
ref={this._refsManager.linkRef('testForm')}
>
<Form.Item label="姓名:" name="name" initValue="李雷">
<Input placeholder="请输入" size="medium" style={{ width: 320 }} />
</Form.Item>
<Form.Item label="年龄:" name="age" initValue="22">
<NumberPicker size="medium" type="normal" />
</Form.Item>
<Form.Item label="职业:" name="profession">
<Select
dataSource={[
{ label: '教师', value: 't' },
{ label: '医生', value: 'd' },
{ label: '歌手', value: 's' },
]}
/>
</Form.Item>
<div style={{ textAlign: 'center' }}>
<Button.Group>
{__$$evalArray(() => ['a', 'b', 'c']).map((item, index) =>
((__$$context) =>
!!__$$eval(() => index >= 1) && (
<Button type="primary" style={{ margin: '0 5px 0 5px' }}>
{__$$eval(() => item)}
</Button>
))(__$$createChildContext(__$$context, { item, index }))
)}
</Button.Group>
</div>
</Form>
</div>
);
}
}
export default Test$$Page;
function __$$eval(expr) {
try {
return expr();
} catch (error) {}
}
function __$$evalArray(expr) {
const res = __$$eval(expr);
return Array.isArray(res) ? res : [];
}
function __$$createChildContext(oldContext, ext) {
const childContext = {
...oldContext,
...ext,
};
childContext.__proto__ = oldContext;
return childContext;
}

View File

@ -0,0 +1,10 @@
import { Outlet } from 'ice';
import BasicLayout from '@/layouts/BasicLayout';
export default function Layout() {
return (
<BasicLayout>
<Outlet />
</BasicLayout>
);
}

View File

@ -0,0 +1,9 @@
/// <reference types="@ice/app/types" />
export {};
declare global {
interface Window {
g_config: Record<string, any>;
}
}

View File

@ -0,0 +1,47 @@
import { createRef } from 'react';
export class RefsManager {
constructor() {
this.refInsStore = {};
}
clearNullRefs() {
Object.keys(this.refInsStore).forEach((refName) => {
const filteredInsList = this.refInsStore[refName].filter(
(insRef) => !!insRef.current
);
if (filteredInsList.length > 0) {
this.refInsStore[refName] = filteredInsList;
} else {
delete this.refInsStore[refName];
}
});
}
get(refName) {
this.clearNullRefs();
if (this.refInsStore[refName] && this.refInsStore[refName].length > 0) {
return this.refInsStore[refName][0].current;
}
return null;
}
getAll(refName) {
this.clearNullRefs();
if (this.refInsStore[refName] && this.refInsStore[refName].length > 0) {
return this.refInsStore[refName].map((i) => i.current);
}
return [];
}
linkRef(refName) {
const refIns = createRef();
this.refInsStore[refName] = this.refInsStore[refName] || [];
this.refInsStore[refName].push(refIns);
return refIns;
}
}
export default {};

View File

@ -0,0 +1,276 @@
{
"version": "1.0.0",
"componentsMap": [
{
"componentName": "Button",
"package": "@alifd/next",
"version": "1.19.18",
"destructuring": true,
"exportName": "Button"
},
{
"componentName": "Button.Group",
"package": "@alifd/next",
"version": "1.19.18",
"destructuring": true,
"exportName": "Button",
"subName": "Group"
},
{
"componentName": "Input",
"package": "@alifd/next",
"version": "1.19.18",
"destructuring": true,
"exportName": "Input"
},
{
"componentName": "Form",
"package": "@alifd/next",
"version": "1.19.18",
"destructuring": true,
"exportName": "Form"
},
{
"componentName": "Form.Item",
"package": "@alifd/next",
"version": "1.19.18",
"destructuring": true,
"exportName": "Form",
"subName": "Item"
},
{
"componentName": "NumberPicker",
"package": "@alifd/next",
"version": "1.19.18",
"destructuring": true,
"exportName": "NumberPicker"
},
{
"componentName": "Select",
"package": "@alifd/next",
"version": "1.19.18",
"destructuring": true,
"exportName": "Select"
}
],
"componentsTree": [
{
"componentName": "Page",
"id": "node$1",
"meta": {
"title": "测试",
"router": "/"
},
"props": {
"ref": "outterView",
"autoLoading": true
},
"fileName": "test",
"state": {
"text": "outter"
},
"lifeCycles": {
"componentDidMount": {
"type": "JSFunction",
"value": "function() { console.log('componentDidMount'); }"
}
},
dataSource: {
list: [
{
id: 'urlParams',
type: 'urlParams',
},
// 示例数据源https://shs.xxx.com/mock/1458/demo/user
{
id: 'user',
type: 'fetch',
options: {
method: 'GET',
uri: 'https://shs.xxx.com/mock/1458/demo/user',
isSync: true,
},
dataHandler: {
type: 'JSExpression',
value: 'function (response) {\nif (!response.data.success){\n throw new Error(response.data.message);\n }\n return response.data.data;\n}',
},
},
// 示例数据源https://shs.xxx.com/mock/1458/demo/orders
{
id: 'orders',
type: 'fetch',
options: {
method: 'GET',
uri: "https://shs.xxx.com/mock/1458/demo/orders",
isSync: true,
},
dataHandler: {
type: 'JSExpression',
value: 'function (response) {\nif (!response.data.success){\n throw new Error(response.data.message);\n }\n return response.data.data.result;\n}',
},
},
],
dataHandler: {
type: 'JSExpression',
value: 'function (dataMap) {\n console.info("All datasources loaded:", dataMap);\n}',
},
},
"children": [
{
"componentName": "Form",
"id": "node$2",
"props": {
"labelCol": {
"type": "JSExpression",
"value": "this.state.colNum"
},
"style": {},
"ref": "testForm"
},
"children": [
{
"componentName": "Form.Item",
"id": "node$3",
"props": {
"label": "姓名:",
"name": "name",
"initValue": "李雷"
},
"children": [
{
"componentName": "Input",
"id": "node$4",
"props": {
"placeholder": "请输入",
"size": "medium",
"style": {
"width": 320
}
}
}
]
},
{
"componentName": "Form.Item",
"id": "node$5",
"props": {
"label": "年龄:",
"name": "age",
"initValue": "22"
},
"children": [
{
"componentName": "NumberPicker",
"id": "node$6",
"props": {
"size": "medium",
"type": "normal"
}
}
]
},
{
"componentName": "Form.Item",
"id": "node$7",
"props": {
"label": "职业:",
"name": "profession"
},
"children": [
{
"componentName": "Select",
"id": "node$8",
"props": {
"dataSource": [
{
"label": "教师",
"value": "t"
},
{
"label": "医生",
"value": "d"
},
{
"label": "歌手",
"value": "s"
}
]
}
}
]
},
{
"componentName": "Div",
"id": "node$9",
"props": {
"style": {
"textAlign": "center"
}
},
"children": [
{
"componentName": "Button.Group",
"id": "node$a",
"props": {},
"children": [
{
"componentName": "Button",
"id": "node$b",
"condition": {
"type": "JSExpression",
"value": "this.index >= 1"
},
"loop": ["a", "b", "c"],
"props": {
"type": "primary",
"style": {
"margin": "0 5px 0 5px"
},
},
"children": [
{
"type": "JSExpression",
"value": "this.item"
}
]
}
]
}
]
}
]
}
]
}
],
"constants": {
"ENV": "prod",
"DOMAIN": "xxx.xxx.com"
},
"css": "body {font-size: 12px;} .table { width: 100px;}",
"config": {
"sdkVersion": "1.0.3",
"historyMode": "hash",
"targetRootID": "J_Container",
"layout": {
"componentName": "BasicLayout",
"props": {
"logo": "...",
"name": "测试网站"
}
},
"theme": {
"package": "@alife/theme-fusion",
"version": "^0.1.0",
"primary": "#ff9966"
}
},
"meta": {
"name": "demo应用",
"git_group": "appGroup",
"project_name": "app_demo",
"description": "这是一个测试应用",
"spma": "spa23d",
"creator": "月飞"
}
}

View File

@ -0,0 +1,25 @@
# See https://help.github.com/ignore-files/ for more about ignoring files.
# dependencies
node_modules/
# production
build/
dist/
tmp/
lib/
# misc
.idea/
.happypack
.DS_Store
*.swp
*.dia~
.ice
npm-debug.log*
yarn-debug.log*
yarn-error.log*
index.module.scss.d.ts

View File

@ -0,0 +1 @@
This project is generated by lowcode-code-generator & lowcode-solution-icejs3.

View File

@ -0,0 +1,90 @@
import { join } from 'path';
import { defineConfig } from '@ice/app';
import _ from 'lodash';
import fusion from '@ice/plugin-fusion';
import locales from '@ice/plugin-moment-locales';
import type { Plugin } from '@ice/app/esm/types';
interface PluginOptions {
id: string;
}
const plugin: Plugin<PluginOptions> = (options) => ({
// name 可选,插件名称
name: 'plugin-name',
// setup 必选,用于定制工程构建配置
setup: ({ onGetConfig, modifyUserConfig }) => {
modifyUserConfig('codeSplitting', 'page');
onGetConfig((config) => {
config.entry = {
web: join(process.cwd(), '.ice/entry.client.tsx'),
};
config.cssFilename = '[name].css';
config.configureWebpack = config.configureWebpack || [];
config.configureWebpack?.push((webpackConfig) => {
if (webpackConfig.output) {
webpackConfig.output.filename = '[name].js';
webpackConfig.output.chunkFilename = '[name].js';
}
return webpackConfig;
});
config.swcOptions = _.merge(config.swcOptions, {
compilationConfig: {
jsc: {
transform: {
react: {
runtime: 'classic',
},
},
},
},
});
// 解决 webpack publicPath 问题
config.transforms = config.transforms || [];
config.transforms.push((source: string, id: string) => {
if (id.includes('.ice/entry.client.tsx')) {
let code = `
if (!__webpack_public_path__?.startsWith('http') && document.currentScript) {
// @ts-ignore
__webpack_public_path__ = document.currentScript.src.replace(/^(.*\\/)[^/]+$/, '$1');
window.__ICE_ASSETS_MANIFEST__ = window.__ICE_ASSETS_MANIFEST__ || {};
window.__ICE_ASSETS_MANIFEST__.publicPath = __webpack_public_path__;
}
`;
code += source;
return { code };
}
});
});
},
});
// The project config, see https://v3.ice.work/docs/guide/basic/config
const minify = process.env.NODE_ENV === 'production' ? 'swc' : false;
export default defineConfig(() => ({
ssr: false,
ssg: false,
minify,
externals: {
react: 'React',
'react-dom': 'ReactDOM',
'react-dom/client': 'ReactDOM',
'@alifd/next': 'Next',
lodash: 'var window._',
'@alilc/lowcode-engine': 'var window.AliLowCodeEngine',
},
plugins: [
fusion({
importStyle: true,
}),
locales(),
plugin(),
],
}));

View File

@ -0,0 +1,48 @@
{
"name": "icejs3-demo-app",
"version": "0.1.5",
"description": "icejs 3 轻量级模板,使用 JavaScript仅包含基础的 Layout。",
"dependencies": {
"moment": "^2.24.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-router": "^6.9.0",
"react-router-dom": "^6.9.0",
"intl-messageformat": "^9.3.6",
"@alifd/next": "1.26.15",
"@ice/runtime": "^1.0.0",
"@alilc/lowcode-datasource-engine": "^1.0.0",
"@alilc/lowcode-datasource-url-params-handler": "^1.0.0",
"@alilc/b6-page": "^0.1.0",
"@alilc/b6-text": "^0.1.0",
"@alilc/b6-compact-legao-builtin": "1.x",
"antd": "3.x",
"@alilc/b6-util-mocks": "1.x"
},
"devDependencies": {
"@ice/app": "^3.0.0",
"@types/react": "^18.0.0",
"@types/react-dom": "^18.0.0",
"@types/node": "^18.11.17",
"@ice/plugin-fusion": "^1.0.1",
"@ice/plugin-moment-locales": "^1.0.0",
"eslint": "^6.0.1",
"stylelint": "^13.2.0"
},
"scripts": {
"start": "ice start",
"build": "ice build",
"lint": "npm run eslint && npm run stylelint",
"eslint": "eslint --cache --ext .js,.jsx ./",
"stylelint": "stylelint ./**/*.scss"
},
"engines": {
"node": ">=14.0.0"
},
"repository": {
"type": "git",
"url": "http://gitlab.xxx.com/msd/leak-scan/tree/master"
},
"private": true,
"originTemplate": "@alifd/scaffold-lite-js"
}

View File

@ -0,0 +1,13 @@
import { defineAppConfig } from 'ice';
// App config, see https://v3.ice.work/docs/guide/basic/app
export default defineAppConfig(() => ({
// Set your configs here.
app: {
rootId: 'App',
},
router: {
type: 'browser',
basename: '/',
},
}));

View File

@ -0,0 +1,3 @@
const __$$constants = {};
export default __$$constants;

View File

@ -0,0 +1,29 @@
import React from 'react';
import { Meta, Title, Links, Main, Scripts } from 'ice';
export default function Document() {
return (
<html>
<head>
<meta charSet="utf-8" />
<meta name="description" content="ice.js 3 lite scaffold" />
<link rel="icon" href="/favicon.ico" />
<link rel="stylesheet" href="//alifd.alicdn.com/npm/@alifd/next/1.21.16/next.min.css" />
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" />
<Meta />
<Title />
<Links />
</head>
<body>
<Main />
<script crossOrigin="anonymous" src="//g.alicdn.com/code/lib/react/18.2.0/umd/react.development.js" />
<script crossOrigin="anonymous" src="//g.alicdn.com/code/lib/react-dom/18.2.0/umd/react-dom.development.js" />
<script crossOrigin="anonymous" src="//g.alicdn.com/code/lib/??react-router/6.9.0/react-router.production.min.js,react-router-dom/6.9.0/react-router-dom.production.min.js" />
<script crossOrigin="anonymous" src="//alifd.alicdn.com/npm/@alifd/next/1.26.15/next.min.js" />
<script crossOrigin="anonymous" src="//g.alicdn.com/code/lib/prop-types/15.7.2/prop-types.js" />
<script crossOrigin="anonymous" src="//g.alicdn.com/platform/c/??lodash/4.6.1/lodash.min.js,immutable/3.7.6/dist/immutable.min.js" />
<Scripts />
</body>
</html>
);
}

View File

@ -0,0 +1,6 @@
// 引入默认全局样式
@import '@alifd/next/reset.scss';
body {
-webkit-font-smoothing: antialiased;
}

View File

@ -0,0 +1,77 @@
const i18nConfig = {};
let locale =
typeof navigator === 'object' && typeof navigator.language === 'string'
? navigator.language
: 'zh-CN';
const getLocale = () => locale;
const setLocale = (target) => {
locale = target;
};
const isEmptyVariables = (variables) =>
(Array.isArray(variables) && variables.length === 0) ||
(typeof variables === 'object' &&
(!variables || Object.keys(variables).length === 0));
// 按低代码规范里面的要求进行变量替换
const format = (msg, variables) =>
typeof msg === 'string'
? msg.replace(/\$\{(\w+)\}/g, (match, key) => variables?.[key] ?? '')
: msg;
const i18nFormat = ({ id, defaultMessage, fallback }, variables) => {
const msg =
i18nConfig[locale]?.[id] ??
i18nConfig[locale.replace('-', '_')]?.[id] ??
defaultMessage;
if (msg == null) {
console.warn('[i18n]: unknown message id: %o (locale=%o)', id, locale);
return fallback === undefined ? `${id}` : fallback;
}
return format(msg, variables);
};
const i18n = (id, params) => {
return i18nFormat({ id }, params);
};
// 将国际化的一些方法注入到目标对象&上下文中
const _inject2 = (target) => {
target.i18n = i18n;
target.getLocale = getLocale;
target.setLocale = (locale) => {
setLocale(locale);
target.forceUpdate();
};
target._i18nText = (t) => {
// 优先取直接传过来的语料
const localMsg = t[locale] ?? t[String(locale).replace('-', '_')];
if (localMsg != null) {
return format(localMsg, t.params);
}
// 其次用项目级别的
const projectMsg = i18nFormat({ id: t.key, fallback: null }, t.params);
if (projectMsg != null) {
return projectMsg;
}
// 兜底用 use 指定的或默认语言的
return format(t[t.use || 'zh-CN'] ?? t.en_US, t.params);
};
// 注入到上下文中去
if (target._context && target._context !== target) {
Object.assign(target._context, {
i18n,
getLocale,
setLocale: target.setLocale,
});
}
};
export { getLocale, setLocale, i18n, i18nFormat, _inject2 };

View File

@ -0,0 +1,14 @@
import React from 'react';
import styles from './index.module.scss';
export default function Footer() {
return (
<p className={styles.footer}>
<span className={styles.logo}>Alibaba Fusion</span>
<br />
<span className={styles.copyright}>© 2019-现在 Alibaba Fusion & ICE</span>
</p>
);
}

View File

@ -0,0 +1,15 @@
.footer {
line-height: 20px;
text-align: center;
}
.logo {
font-weight: bold;
font-size: 16px;
}
.copyright {
font-size: 12px;
}

View File

@ -0,0 +1,16 @@
import React from 'react';
import { Link } from 'ice';
import styles from './index.module.scss';
export default function Logo({ image, text, url }) {
return (
<div className="logo">
<Link to={url || '/'} className={styles.logo}>
{image && <img src={image} alt="logo" />}
<span>{text}</span>
</Link>
</div>
);
}

View File

@ -0,0 +1,20 @@
.logo{
display: flex;
align-items: center;
justify-content: center;
color: $color-text1-1;
font-weight: bold;
font-size: 14px;
line-height: 22px;
&:visited, &:link {
color: $color-text1-1;
}
img {
height: 24px;
margin-right: 10px;
}
}

View File

@ -0,0 +1,68 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Link, useLocation } from 'ice';
import { Nav } from '@alifd/next';
import { asideMenuConfig } from '../../menuConfig';
const { SubNav } = Nav;
const NavItem = Nav.Item;
function getNavMenuItems(menusData) {
if (!menusData) {
return [];
}
return menusData
.filter(item => item.name && !item.hideInMenu)
.map((item, index) => getSubMenuOrItem(item, index));
}
function getSubMenuOrItem(item, index) {
if (item.children && item.children.some(child => child.name)) {
const childrenItems = getNavMenuItems(item.children);
if (childrenItems && childrenItems.length > 0) {
const subNav = (
<SubNav key={index} icon={item.icon} label={item.name}>
{childrenItems}
</SubNav>
);
return subNav;
}
return null;
}
const navItem = (
<NavItem key={item.path} icon={item.icon}>
<Link to={item.path}>{item.name}</Link>
</NavItem>
);
return navItem;
}
const Navigation = (props, context) => {
const location = useLocation();
const { pathname } = location;
const { isCollapse } = context;
return (
<Nav
type="primary"
selectedKeys={[pathname]}
defaultSelectedKeys={[pathname]}
embeddable
openMode="single"
iconOnly={isCollapse}
hasArrow={false}
mode={isCollapse ? 'popup' : 'inline'}
>
{getNavMenuItems(asideMenuConfig)}
</Nav>
);
};
Navigation.contextTypes = {
isCollapse: PropTypes.bool,
};
export default Navigation;

View File

@ -0,0 +1,81 @@
import React, { useState } from 'react';
import { Shell, ConfigProvider } from '@alifd/next';
import PageNav from './components/PageNav';
import Logo from './components/Logo';
import Footer from './components/Footer';
(function() {
const throttle = function(type, name, obj = window) {
let running = false;
const func = () => {
if (running) {
return;
}
running = true;
requestAnimationFrame(() => {
obj.dispatchEvent(new CustomEvent(name));
running = false;
});
};
obj.addEventListener(type, func);
};
throttle('resize', 'optimizedResize');
})();
export default function BasicLayout({ children }) {
const getDevice = width => {
const isPhone =
typeof navigator !== 'undefined' && navigator && navigator.userAgent.match(/phone/gi);
if (width < 680 || isPhone) {
return 'phone';
}
if (width < 1280 && width > 680) {
return 'tablet';
}
return 'desktop';
};
const [device, setDevice] = useState(getDevice(NaN));
window.addEventListener('optimizedResize', e => {
setDevice(getDevice(e && e.target && e.target.innerWidth));
});
return (
<ConfigProvider device={device}>
<Shell
type="dark"
style={{
minHeight: '100vh',
}}
>
<Shell.Branding>
<Logo
image="https://img.alicdn.com/tfs/TB1.ZBecq67gK0jSZFHXXa9jVXa-904-826.png"
text="Logo"
/>
</Shell.Branding>
<Shell.Navigation
direction="hoz"
style={{
marginRight: 10,
}}
></Shell.Navigation>
<Shell.Action></Shell.Action>
<Shell.Navigation>
<PageNav />
</Shell.Navigation>
<Shell.Content>{children}</Shell.Content>
<Shell.Footer>
<Footer />
</Shell.Footer>
</Shell>
</ConfigProvider>
);
}

View File

@ -0,0 +1,11 @@
const headerMenuConfig = [];
const asideMenuConfig = [
{
name: 'Dashboard',
path: '/',
icon: 'smile',
},
];
export { headerMenuConfig, asideMenuConfig };

View File

@ -0,0 +1,118 @@
// : "__$$" 访
// react
import React from 'react';
import { Page } from '@alilc/b6-page';
import { Text } from '@alilc/b6-text';
import { createUrlParamsHandler as __$$createUrlParamsRequestHandler } from '@alilc/lowcode-datasource-url-params-handler';
import { create as __$$createDataSourceEngine } from '@alilc/lowcode-datasource-engine/runtime';
import utils from '../../utils';
import * as __$$i18n from '../../i18n';
import __$$constants from '../../constants';
import './index.css';
class Aaaa$$Page extends React.Component {
_context = this;
_dataSourceConfig = this._defineDataSourceConfig();
_dataSourceEngine = __$$createDataSourceEngine(this._dataSourceConfig, this, {
runtimeConfig: true,
requestHandlersMap: {
urlParams: __$$createUrlParamsRequestHandler(window.location.search),
},
});
get dataSourceMap() {
return this._dataSourceEngine.dataSourceMap || {};
}
reloadDataSource = async () => {
await this._dataSourceEngine.reloadDataSource();
};
get constants() {
return __$$constants || {};
}
constructor(props, context) {
super(props);
this.utils = utils;
__$$i18n._inject2(this);
this.state = {};
}
$ = () => null;
$$ = () => [];
_defineDataSourceConfig() {
const _this = this;
return {
list: [
{
id: 'urlParams',
type: 'urlParams',
description: 'URL参数',
options: function () {
return {
uri: '',
};
}.bind(_this),
isInit: function () {
return undefined;
}.bind(_this),
},
],
};
}
componentDidMount() {
this._dataSourceEngine.reloadDataSource();
}
render() {
const __$$context = this._context || this;
const { state } = __$$context;
return (
<div title="" backgroundColor="#fff" textColor="#333" style={{}}>
<Text
content="欢迎使用 BuildSuccesssadsad"
style={{}}
fieldId="text_kp6ci11t"
/>
</div>
);
}
}
export default Aaaa$$Page;
function __$$eval(expr) {
try {
return expr();
} catch (error) {}
}
function __$$evalArray(expr) {
const res = __$$eval(expr);
return Array.isArray(res) ? res : [];
}
function __$$createChildContext(oldContext, ext) {
const childContext = {
...oldContext,
...ext,
};
childContext.__proto__ = oldContext;
return childContext;
}

View File

@ -0,0 +1,10 @@
import { Outlet } from 'ice';
import BasicLayout from '@/layouts/BasicLayout';
export default function Layout() {
return (
<BasicLayout>
<Outlet />
</BasicLayout>
);
}

View File

@ -0,0 +1,9 @@
/// <reference types="@ice/app/types" />
export {};
declare global {
interface Window {
g_config: Record<string, any>;
}
}

View File

@ -0,0 +1,61 @@
import legaoBuiltin from '@alilc/b6-compact-legao-builtin';
import { message, Modal as modal } from 'antd';
import { mocks } from '@alilc/b6-util-mocks';
import { createRef } from 'react';
export class RefsManager {
constructor() {
this.refInsStore = {};
}
clearNullRefs() {
Object.keys(this.refInsStore).forEach((refName) => {
const filteredInsList = this.refInsStore[refName].filter(
(insRef) => !!insRef.current
);
if (filteredInsList.length > 0) {
this.refInsStore[refName] = filteredInsList;
} else {
delete this.refInsStore[refName];
}
});
}
get(refName) {
this.clearNullRefs();
if (this.refInsStore[refName] && this.refInsStore[refName].length > 0) {
return this.refInsStore[refName][0].current;
}
return null;
}
getAll(refName) {
this.clearNullRefs();
if (this.refInsStore[refName] && this.refInsStore[refName].length > 0) {
return this.refInsStore[refName].map((i) => i.current);
}
return [];
}
linkRef(refName) {
const refIns = createRef();
this.refInsStore[refName] = this.refInsStore[refName] || [];
this.refInsStore[refName].push(refIns);
return refIns;
}
}
export default {
legaoBuiltin,
message,
mocks,
modal,
};

View File

@ -0,0 +1,123 @@
{
version: '1.0.0',
componentsMap: [
{
package: '@alilc/b6-page',
version: '^0.1.0',
componentName: 'Page',
destructuring: true,
exportName: 'Page',
},
{
package: '@alilc/b6-text',
version: '^0.1.0',
componentName: 'Text',
destructuring: true,
exportName: 'Text',
},
],
componentsTree: [
{
componentName: 'Page',
id: 'node_ockp6ci0hm1',
props: {
title: '',
backgroundColor: '#fff',
textColor: '#333',
style: {},
},
fileName: 'aaaa',
dataSource: {
list: [
{
id: 'urlParams',
type: 'urlParams',
description: 'URL参数',
options: {
uri: '',
},
},
],
},
children: [
{
componentName: 'Text',
id: 'node_ockp6ci0hm2',
props: {
content: '欢迎使用 BuildSuccesssadsad',
style: {},
fieldId: 'text_kp6ci11t',
},
},
],
meta: {
router: '/aaaa',
},
methodsModule: {
type: 'JSModule',
compiled: '"use strict";\n\nObject.defineProperty(exports, "__esModule", {\n value: true\n});\nexports.helloPage = helloPage;\n\n/**\n * Private, and can be re-used functions\n * Actions panel help documentation\n * @see https://yuque.antfin.com/docs/share/89ca7965-6387-4e3a-9964-81929ed48f1e\n */\nfunction printLog(obj) {\n console.info(obj);\n}\n/**\n * page function\n */\n\n\nfunction helloPage() {\n console.log(\'hello page\');\n}',
source: "/**\n * Private, and can be re-used functions\n * Actions panel help documentation\n * @see https://yuque.antfin.com/docs/share/89ca7965-6387-4e3a-9964-81929ed48f1e\n */\nfunction printLog(obj) {\n console.info(obj);\n}\n\n/**\n * page function\n */\nexport function helloPage() {\n console.log('hello page');\n}",
},
},
],
i18n: {},
utils: [
{
name: 'legaoBuiltin',
type: 'npm',
content: {
exportName: 'legaoBuiltin',
package: '@alilc/b6-compact-legao-builtin',
version: '1.x',
},
},
{
name: 'message',
type: 'npm',
content: {
package: 'antd',
version: '3.x',
destructuring: true,
exportName: 'message',
},
},
{
name: 'mocks',
type: 'npm',
content: {
package: '@alilc/b6-util-mocks',
version: '1.x',
exportName: 'mocks',
destructuring: true,
},
},
{
name: 'modal',
type: 'npm',
content: {
package: 'antd',
version: '3.x',
destructuring: true,
exportName: 'Modal',
},
},
],
constants: {},
dataSource: {
list: [],
},
config: {
sdkVersion: '1.0.3',
historyMode: 'hash',
targetRootID: 'root',
miniAppBuildType: 'runtime',
},
meta: {
name: 'jinyuan-test2',
git_group: 'b6',
project_name: 'jinyuan-test2',
description: '瑾源测试',
spma: 'spmademo',
creator: '张三',
},
}

View File

@ -0,0 +1,3 @@
defaults
ios_saf 9

View File

@ -0,0 +1,25 @@
# See https://help.github.com/ignore-files/ for more about ignoring files.
# dependencies
node_modules/
# production
build/
dist/
tmp/
lib/
# misc
.idea/
.happypack
.DS_Store
*.swp
*.dia~
.ice
npm-debug.log*
yarn-debug.log*
yarn-error.log*
index.module.scss.d.ts

View File

@ -0,0 +1 @@
This project is generated by lowcode-code-generator & lowcode-solution-icejs3.

View File

@ -0,0 +1,90 @@
import { join } from 'path';
import { defineConfig } from '@ice/app';
import _ from 'lodash';
import fusion from '@ice/plugin-fusion';
import locales from '@ice/plugin-moment-locales';
import type { Plugin } from '@ice/app/esm/types';
interface PluginOptions {
id: string;
}
const plugin: Plugin<PluginOptions> = (options) => ({
// name 可选,插件名称
name: 'plugin-name',
// setup 必选,用于定制工程构建配置
setup: ({ onGetConfig, modifyUserConfig }) => {
modifyUserConfig('codeSplitting', 'page');
onGetConfig((config) => {
config.entry = {
web: join(process.cwd(), '.ice/entry.client.tsx'),
};
config.cssFilename = '[name].css';
config.configureWebpack = config.configureWebpack || [];
config.configureWebpack?.push((webpackConfig) => {
if (webpackConfig.output) {
webpackConfig.output.filename = '[name].js';
webpackConfig.output.chunkFilename = '[name].js';
}
return webpackConfig;
});
config.swcOptions = _.merge(config.swcOptions, {
compilationConfig: {
jsc: {
transform: {
react: {
runtime: 'classic',
},
},
},
},
});
// 解决 webpack publicPath 问题
config.transforms = config.transforms || [];
config.transforms.push((source: string, id: string) => {
if (id.includes('.ice/entry.client.tsx')) {
let code = `
if (!__webpack_public_path__?.startsWith('http') && document.currentScript) {
// @ts-ignore
__webpack_public_path__ = document.currentScript.src.replace(/^(.*\\/)[^/]+$/, '$1');
window.__ICE_ASSETS_MANIFEST__ = window.__ICE_ASSETS_MANIFEST__ || {};
window.__ICE_ASSETS_MANIFEST__.publicPath = __webpack_public_path__;
}
`;
code += source;
return { code };
}
});
});
},
});
// The project config, see https://v3.ice.work/docs/guide/basic/config
const minify = process.env.NODE_ENV === 'production' ? 'swc' : false;
export default defineConfig(() => ({
ssr: false,
ssg: false,
minify,
externals: {
react: 'React',
'react-dom': 'ReactDOM',
'react-dom/client': 'ReactDOM',
'@alifd/next': 'Next',
lodash: 'var window._',
'@alilc/lowcode-engine': 'var window.AliLowCodeEngine',
},
plugins: [
fusion({
importStyle: true,
}),
locales(),
plugin(),
],
}));

View File

@ -0,0 +1,42 @@
{
"name": "icejs3-demo-app",
"version": "0.1.5",
"description": "icejs 3 轻量级模板,使用 JavaScript仅包含基础的 Layout。",
"dependencies": {
"moment": "^2.24.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-router": "^6.9.0",
"react-router-dom": "^6.9.0",
"intl-messageformat": "^9.3.6",
"@alifd/next": "1.19.18",
"@ice/runtime": "^1.0.0",
"@alilc/lowcode-datasource-engine": "^1.0.0"
},
"devDependencies": {
"@ice/app": "^3.0.0",
"@types/react": "^18.0.0",
"@types/react-dom": "^18.0.0",
"@types/node": "^18.11.17",
"@ice/plugin-fusion": "^1.0.1",
"@ice/plugin-moment-locales": "^1.0.0",
"eslint": "^6.0.1",
"stylelint": "^13.2.0"
},
"scripts": {
"start": "ice start",
"build": "ice build",
"lint": "npm run eslint && npm run stylelint",
"eslint": "eslint --cache --ext .js,.jsx ./",
"stylelint": "stylelint ./**/*.scss"
},
"engines": {
"node": ">=14.0.0"
},
"repository": {
"type": "git",
"url": "http://gitlab.xxx.com/msd/leak-scan/tree/master"
},
"private": true,
"originTemplate": "@alifd/scaffold-lite-js"
}

View File

@ -0,0 +1,13 @@
import { defineAppConfig } from 'ice';
// App config, see https://v3.ice.work/docs/guide/basic/app
export default defineAppConfig(() => ({
// Set your configs here.
app: {
rootId: 'App',
},
router: {
type: 'browser',
basename: '/',
},
}));

View File

@ -0,0 +1,3 @@
const __$$constants = { ENV: 'prod', DOMAIN: 'xxx.xxx.com' };
export default __$$constants;

View File

@ -0,0 +1,29 @@
import React from 'react';
import { Meta, Title, Links, Main, Scripts } from 'ice';
export default function Document() {
return (
<html>
<head>
<meta charSet="utf-8" />
<meta name="description" content="ice.js 3 lite scaffold" />
<link rel="icon" href="/favicon.ico" />
<link rel="stylesheet" href="//alifd.alicdn.com/npm/@alifd/next/1.21.16/next.min.css" />
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" />
<Meta />
<Title />
<Links />
</head>
<body>
<Main />
<script crossOrigin="anonymous" src="//g.alicdn.com/code/lib/react/18.2.0/umd/react.development.js" />
<script crossOrigin="anonymous" src="//g.alicdn.com/code/lib/react-dom/18.2.0/umd/react-dom.development.js" />
<script crossOrigin="anonymous" src="//g.alicdn.com/code/lib/??react-router/6.9.0/react-router.production.min.js,react-router-dom/6.9.0/react-router-dom.production.min.js" />
<script crossOrigin="anonymous" src="//alifd.alicdn.com/npm/@alifd/next/1.26.15/next.min.js" />
<script crossOrigin="anonymous" src="//g.alicdn.com/code/lib/prop-types/15.7.2/prop-types.js" />
<script crossOrigin="anonymous" src="//g.alicdn.com/platform/c/??lodash/4.6.1/lodash.min.js,immutable/3.7.6/dist/immutable.min.js" />
<Scripts />
</body>
</html>
);
}

View File

@ -0,0 +1,13 @@
// 引入默认全局样式
@import '@alifd/next/reset.scss';
body {
-webkit-font-smoothing: antialiased;
}
body {
font-size: 12px;
}
.table {
width: 100px;
}

View File

@ -0,0 +1,86 @@
const i18nConfig = {
'zh-CN': {
'i18n-jwg27yo4': '你好',
'i18n-jwg27yo3': '中国',
},
'en-US': {
'i18n-jwg27yo4': 'Hello',
'i18n-jwg27yo3': 'China',
},
};
let locale =
typeof navigator === 'object' && typeof navigator.language === 'string'
? navigator.language
: 'zh-CN';
const getLocale = () => locale;
const setLocale = (target) => {
locale = target;
};
const isEmptyVariables = (variables) =>
(Array.isArray(variables) && variables.length === 0) ||
(typeof variables === 'object' &&
(!variables || Object.keys(variables).length === 0));
// 按低代码规范里面的要求进行变量替换
const format = (msg, variables) =>
typeof msg === 'string'
? msg.replace(/\$\{(\w+)\}/g, (match, key) => variables?.[key] ?? '')
: msg;
const i18nFormat = ({ id, defaultMessage, fallback }, variables) => {
const msg =
i18nConfig[locale]?.[id] ??
i18nConfig[locale.replace('-', '_')]?.[id] ??
defaultMessage;
if (msg == null) {
console.warn('[i18n]: unknown message id: %o (locale=%o)', id, locale);
return fallback === undefined ? `${id}` : fallback;
}
return format(msg, variables);
};
const i18n = (id, params) => {
return i18nFormat({ id }, params);
};
// 将国际化的一些方法注入到目标对象&上下文中
const _inject2 = (target) => {
target.i18n = i18n;
target.getLocale = getLocale;
target.setLocale = (locale) => {
setLocale(locale);
target.forceUpdate();
};
target._i18nText = (t) => {
// 优先取直接传过来的语料
const localMsg = t[locale] ?? t[String(locale).replace('-', '_')];
if (localMsg != null) {
return format(localMsg, t.params);
}
// 其次用项目级别的
const projectMsg = i18nFormat({ id: t.key, fallback: null }, t.params);
if (projectMsg != null) {
return projectMsg;
}
// 兜底用 use 指定的或默认语言的
return format(t[t.use || 'zh-CN'] ?? t.en_US, t.params);
};
// 注入到上下文中去
if (target._context && target._context !== target) {
Object.assign(target._context, {
i18n,
getLocale,
setLocale: target.setLocale,
});
}
};
export { getLocale, setLocale, i18n, i18nFormat, _inject2 };

View File

@ -0,0 +1,14 @@
import React from 'react';
import styles from './index.module.scss';
export default function Footer() {
return (
<p className={styles.footer}>
<span className={styles.logo}>Alibaba Fusion</span>
<br />
<span className={styles.copyright}>© 2019-现在 Alibaba Fusion & ICE</span>
</p>
);
}

View File

@ -0,0 +1,15 @@
.footer {
line-height: 20px;
text-align: center;
}
.logo {
font-weight: bold;
font-size: 16px;
}
.copyright {
font-size: 12px;
}

View File

@ -0,0 +1,16 @@
import React from 'react';
import { Link } from 'ice';
import styles from './index.module.scss';
export default function Logo({ image, text, url }) {
return (
<div className="logo">
<Link to={url || '/'} className={styles.logo}>
{image && <img src={image} alt="logo" />}
<span>{text}</span>
</Link>
</div>
);
}

View File

@ -0,0 +1,20 @@
.logo{
display: flex;
align-items: center;
justify-content: center;
color: $color-text1-1;
font-weight: bold;
font-size: 14px;
line-height: 22px;
&:visited, &:link {
color: $color-text1-1;
}
img {
height: 24px;
margin-right: 10px;
}
}

View File

@ -0,0 +1,68 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Link, useLocation } from 'ice';
import { Nav } from '@alifd/next';
import { asideMenuConfig } from '../../menuConfig';
const { SubNav } = Nav;
const NavItem = Nav.Item;
function getNavMenuItems(menusData) {
if (!menusData) {
return [];
}
return menusData
.filter(item => item.name && !item.hideInMenu)
.map((item, index) => getSubMenuOrItem(item, index));
}
function getSubMenuOrItem(item, index) {
if (item.children && item.children.some(child => child.name)) {
const childrenItems = getNavMenuItems(item.children);
if (childrenItems && childrenItems.length > 0) {
const subNav = (
<SubNav key={index} icon={item.icon} label={item.name}>
{childrenItems}
</SubNav>
);
return subNav;
}
return null;
}
const navItem = (
<NavItem key={item.path} icon={item.icon}>
<Link to={item.path}>{item.name}</Link>
</NavItem>
);
return navItem;
}
const Navigation = (props, context) => {
const location = useLocation();
const { pathname } = location;
const { isCollapse } = context;
return (
<Nav
type="primary"
selectedKeys={[pathname]}
defaultSelectedKeys={[pathname]}
embeddable
openMode="single"
iconOnly={isCollapse}
hasArrow={false}
mode={isCollapse ? 'popup' : 'inline'}
>
{getNavMenuItems(asideMenuConfig)}
</Nav>
);
};
Navigation.contextTypes = {
isCollapse: PropTypes.bool,
};
export default Navigation;

View File

@ -0,0 +1,81 @@
import React, { useState } from 'react';
import { Shell, ConfigProvider } from '@alifd/next';
import PageNav from './components/PageNav';
import Logo from './components/Logo';
import Footer from './components/Footer';
(function() {
const throttle = function(type, name, obj = window) {
let running = false;
const func = () => {
if (running) {
return;
}
running = true;
requestAnimationFrame(() => {
obj.dispatchEvent(new CustomEvent(name));
running = false;
});
};
obj.addEventListener(type, func);
};
throttle('resize', 'optimizedResize');
})();
export default function BasicLayout({ children }) {
const getDevice = width => {
const isPhone =
typeof navigator !== 'undefined' && navigator && navigator.userAgent.match(/phone/gi);
if (width < 680 || isPhone) {
return 'phone';
}
if (width < 1280 && width > 680) {
return 'tablet';
}
return 'desktop';
};
const [device, setDevice] = useState(getDevice(NaN));
window.addEventListener('optimizedResize', e => {
setDevice(getDevice(e && e.target && e.target.innerWidth));
});
return (
<ConfigProvider device={device}>
<Shell
type="dark"
style={{
minHeight: '100vh',
}}
>
<Shell.Branding>
<Logo
image="https://img.alicdn.com/tfs/TB1.ZBecq67gK0jSZFHXXa9jVXa-904-826.png"
text="Logo"
/>
</Shell.Branding>
<Shell.Navigation
direction="hoz"
style={{
marginRight: 10,
}}
></Shell.Navigation>
<Shell.Action></Shell.Action>
<Shell.Navigation>
<PageNav />
</Shell.Navigation>
<Shell.Content>{children}</Shell.Content>
<Shell.Footer>
<Footer />
</Shell.Footer>
</Shell>
</ConfigProvider>
);
}

View File

@ -0,0 +1,11 @@
const headerMenuConfig = [];
const asideMenuConfig = [
{
name: 'Dashboard',
path: '/',
icon: 'smile',
},
];
export { headerMenuConfig, asideMenuConfig };

View File

@ -0,0 +1,119 @@
// : "__$$" 访
// react
import React from 'react';
import { Form, Input, NumberPicker, Select, Button } from '@alifd/next';
import utils, { RefsManager } from '../../utils';
import * as __$$i18n from '../../i18n';
import __$$constants from '../../constants';
import './index.css';
class Test$$Page extends React.Component {
_context = this;
get constants() {
return __$$constants || {};
}
constructor(props, context) {
super(props);
this.utils = utils;
this._refsManager = new RefsManager();
__$$i18n._inject2(this);
this.state = { text: 'outter' };
}
$ = (refName) => {
return this._refsManager.get(refName);
};
$$ = (refName) => {
return this._refsManager.getAll(refName);
};
componentDidMount() {
console.log('componentDidMount');
}
render() {
const __$$context = this._context || this;
const { state } = __$$context;
return (
<div ref={this._refsManager.linkRef('outterView')} autoLoading={true}>
<Form
labelCol={__$$eval(() => this.state.colNum)}
style={{}}
ref={this._refsManager.linkRef('testForm')}
>
<Form.Item
label={__$$eval(() => this.i18n('i18n-jwg27yo4'))}
name="name"
initValue="李雷"
>
<Input placeholder="请输入" size="medium" style={{ width: 320 }} />
</Form.Item>
<Form.Item label="年龄:" name="age" initValue="22">
<NumberPicker size="medium" type="normal" />
</Form.Item>
<Form.Item label="职业:" name="profession">
<Select
dataSource={[
{ label: '教师', value: 't' },
{ label: '医生', value: 'd' },
{ label: '歌手', value: 's' },
]}
/>
</Form.Item>
<div style={{ textAlign: 'center' }}>
<Button.Group>
<Button
type="primary"
style={{ margin: '0 5px 0 5px' }}
htmlType="submit"
>
提交
</Button>
<Button
type="normal"
style={{ margin: '0 5px 0 5px' }}
htmlType="reset"
>
重置
</Button>
</Button.Group>
</div>
</Form>
</div>
);
}
}
export default Test$$Page;
function __$$eval(expr) {
try {
return expr();
} catch (error) {}
}
function __$$evalArray(expr) {
const res = __$$eval(expr);
return Array.isArray(res) ? res : [];
}
function __$$createChildContext(oldContext, ext) {
const childContext = {
...oldContext,
...ext,
};
childContext.__proto__ = oldContext;
return childContext;
}

View File

@ -0,0 +1,10 @@
import { Outlet } from 'ice';
import BasicLayout from '@/layouts/BasicLayout';
export default function Layout() {
return (
<BasicLayout>
<Outlet />
</BasicLayout>
);
}

View File

@ -0,0 +1,9 @@
/// <reference types="@ice/app/types" />
export {};
declare global {
interface Window {
g_config: Record<string, any>;
}
}

View File

@ -0,0 +1,47 @@
import { createRef } from 'react';
export class RefsManager {
constructor() {
this.refInsStore = {};
}
clearNullRefs() {
Object.keys(this.refInsStore).forEach((refName) => {
const filteredInsList = this.refInsStore[refName].filter(
(insRef) => !!insRef.current
);
if (filteredInsList.length > 0) {
this.refInsStore[refName] = filteredInsList;
} else {
delete this.refInsStore[refName];
}
});
}
get(refName) {
this.clearNullRefs();
if (this.refInsStore[refName] && this.refInsStore[refName].length > 0) {
return this.refInsStore[refName][0].current;
}
return null;
}
getAll(refName) {
this.clearNullRefs();
if (this.refInsStore[refName] && this.refInsStore[refName].length > 0) {
return this.refInsStore[refName].map((i) => i.current);
}
return [];
}
linkRef(refName) {
const refIns = createRef();
this.refInsStore[refName] = this.refInsStore[refName] || [];
this.refInsStore[refName].push(refIns);
return refIns;
}
}
export default {};

View File

@ -0,0 +1,256 @@
{
"version": "1.0.0",
"componentsMap": [
{
"componentName": "Button",
"package": "@alifd/next",
"version": "1.19.18",
"destructuring": true,
"exportName": "Button"
},
{
"componentName": "Button.Group",
"package": "@alifd/next",
"version": "1.19.18",
"destructuring": true,
"exportName": "Button",
"subName": "Group"
},
{
"componentName": "Input",
"package": "@alifd/next",
"version": "1.19.18",
"destructuring": true,
"exportName": "Input"
},
{
"componentName": "Form",
"package": "@alifd/next",
"version": "1.19.18",
"destructuring": true,
"exportName": "Form"
},
{
"componentName": "Form.Item",
"package": "@alifd/next",
"version": "1.19.18",
"destructuring": true,
"exportName": "Form",
"subName": "Item"
},
{
"componentName": "NumberPicker",
"package": "@alifd/next",
"version": "1.19.18",
"destructuring": true,
"exportName": "NumberPicker"
},
{
"componentName": "Select",
"package": "@alifd/next",
"version": "1.19.18",
"destructuring": true,
"exportName": "Select"
}
],
"componentsTree": [
{
"componentName": "Page",
"id": "node$1",
"meta": {
"title": "测试",
"router": "/"
},
"props": {
"ref": "outterView",
"autoLoading": true
},
"fileName": "test",
"state": {
"text": "outter"
},
"lifeCycles": {
"componentDidMount": {
"type": "JSFunction",
"value": "function() { console.log('componentDidMount'); }"
}
},
"children": [
{
"componentName": "Form",
"id": "node$2",
"props": {
"labelCol": {
"type": "JSExpression",
"value": "this.state.colNum"
},
"style": {},
"ref": "testForm"
},
"children": [
{
"componentName": "Form.Item",
"id": "node$3",
"props": {
"label": {
type: 'JSExpression',
value: 'this.i18n("i18n-jwg27yo4")',
},
"name": "name",
"initValue": "李雷"
},
"children": [
{
"componentName": "Input",
"id": "node$4",
"props": {
"placeholder": "请输入",
"size": "medium",
"style": {
"width": 320
}
}
}
]
},
{
"componentName": "Form.Item",
"id": "node$5",
"props": {
"label": "年龄:",
"name": "age",
"initValue": "22"
},
"children": [
{
"componentName": "NumberPicker",
"id": "node$6",
"props": {
"size": "medium",
"type": "normal"
}
}
]
},
{
"componentName": "Form.Item",
"id": "node$7",
"props": {
"label": "职业:",
"name": "profession"
},
"children": [
{
"componentName": "Select",
"id": "node$8",
"props": {
"dataSource": [
{
"label": "教师",
"value": "t"
},
{
"label": "医生",
"value": "d"
},
{
"label": "歌手",
"value": "s"
}
]
}
}
]
},
{
"componentName": "Div",
"id": "node$9",
"props": {
"style": {
"textAlign": "center"
}
},
"children": [
{
"componentName": "Button.Group",
"id": "node$a",
"props": {},
"children": [
{
"componentName": "Button",
"id": "node$b",
"props": {
"type": "primary",
"style": {
"margin": "0 5px 0 5px"
},
"htmlType": "submit"
},
"children": [
"提交"
]
},
{
"componentName": "Button",
"id": "node$d",
"props": {
"type": "normal",
"style": {
"margin": "0 5px 0 5px"
},
"htmlType": "reset"
},
"children": [
"重置"
]
}
]
}
]
}
]
}
]
}
],
"constants": {
"ENV": "prod",
"DOMAIN": "xxx.xxx.com"
},
"i18n": {
"zh-CN": {
"i18n-jwg27yo4": "你好",
"i18n-jwg27yo3": "中国"
},
"en-US": {
"i18n-jwg27yo4": "Hello",
"i18n-jwg27yo3": "China"
}
},
"css": "body {font-size: 12px;} .table { width: 100px;}",
"config": {
"sdkVersion": "1.0.3",
"historyMode": "hash",
"targetRootID": "J_Container",
"layout": {
"componentName": "BasicLayout",
"props": {
"logo": "...",
"name": "测试网站"
}
},
"theme": {
"package": "@alife/theme-fusion",
"version": "^0.1.0",
"primary": "#ff9966"
}
},
"meta": {
"name": "demo应用",
"git_group": "appGroup",
"project_name": "app_demo",
"description": "这是一个测试应用",
"spma": "spa23d",
"creator": "月飞"
}
}

View File

@ -0,0 +1,3 @@
defaults
ios_saf 9

View File

@ -0,0 +1,25 @@
# See https://help.github.com/ignore-files/ for more about ignoring files.
# dependencies
node_modules/
# production
build/
dist/
tmp/
lib/
# misc
.idea/
.happypack
.DS_Store
*.swp
*.dia~
.ice
npm-debug.log*
yarn-debug.log*
yarn-error.log*
index.module.scss.d.ts

View File

@ -0,0 +1 @@
This project is generated by lowcode-code-generator & lowcode-solution-icejs3.

View File

@ -0,0 +1,90 @@
import { join } from 'path';
import { defineConfig } from '@ice/app';
import _ from 'lodash';
import fusion from '@ice/plugin-fusion';
import locales from '@ice/plugin-moment-locales';
import type { Plugin } from '@ice/app/esm/types';
interface PluginOptions {
id: string;
}
const plugin: Plugin<PluginOptions> = (options) => ({
// name 可选,插件名称
name: 'plugin-name',
// setup 必选,用于定制工程构建配置
setup: ({ onGetConfig, modifyUserConfig }) => {
modifyUserConfig('codeSplitting', 'page');
onGetConfig((config) => {
config.entry = {
web: join(process.cwd(), '.ice/entry.client.tsx'),
};
config.cssFilename = '[name].css';
config.configureWebpack = config.configureWebpack || [];
config.configureWebpack?.push((webpackConfig) => {
if (webpackConfig.output) {
webpackConfig.output.filename = '[name].js';
webpackConfig.output.chunkFilename = '[name].js';
}
return webpackConfig;
});
config.swcOptions = _.merge(config.swcOptions, {
compilationConfig: {
jsc: {
transform: {
react: {
runtime: 'classic',
},
},
},
},
});
// 解决 webpack publicPath 问题
config.transforms = config.transforms || [];
config.transforms.push((source: string, id: string) => {
if (id.includes('.ice/entry.client.tsx')) {
let code = `
if (!__webpack_public_path__?.startsWith('http') && document.currentScript) {
// @ts-ignore
__webpack_public_path__ = document.currentScript.src.replace(/^(.*\\/)[^/]+$/, '$1');
window.__ICE_ASSETS_MANIFEST__ = window.__ICE_ASSETS_MANIFEST__ || {};
window.__ICE_ASSETS_MANIFEST__.publicPath = __webpack_public_path__;
}
`;
code += source;
return { code };
}
});
});
},
});
// The project config, see https://v3.ice.work/docs/guide/basic/config
const minify = process.env.NODE_ENV === 'production' ? 'swc' : false;
export default defineConfig(() => ({
ssr: false,
ssg: false,
minify,
externals: {
react: 'React',
'react-dom': 'ReactDOM',
'react-dom/client': 'ReactDOM',
'@alifd/next': 'Next',
lodash: 'var window._',
'@alilc/lowcode-engine': 'var window.AliLowCodeEngine',
},
plugins: [
fusion({
importStyle: true,
}),
locales(),
plugin(),
],
}));

Some files were not shown because too many files have changed in this diff Show More