mirror of https://github.com/alibaba/ice.git
test: improve test case (#497)
* test: improve test case * test: compat win32 * test: test case * test: add test case * test: add test case * test: test case for rax-compat * test: test case for routes * fix: types error * fix: to strict equal * chore: remove unused props * fix: types * test: test case for run client app * chore: remove empty router * test: test case of run server app * chore: remove log * chore: do not remove file by test case * fix: remove document dependency when run client app * chore: lint * chore: remove config * fix: lint * chore: lint warning * fix: test case * chore: update unplugin
This commit is contained in:
parent
c0c342d5d5
commit
ad531405a8
|
|
@ -1,7 +1,6 @@
|
|||
# 忽略目录
|
||||
build/
|
||||
test/
|
||||
tests/
|
||||
fixtures/
|
||||
node_modules/
|
||||
dist/
|
||||
out/
|
||||
|
|
@ -11,7 +10,6 @@ compiled/
|
|||
coverage/
|
||||
|
||||
# 忽略测试文件
|
||||
/packages/*/__tests__
|
||||
/packages/*/lib/
|
||||
/packages/*/esm/
|
||||
/packages/*/es2017/
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ const commonRules = {
|
|||
'no-multiple-empty-lines': 1,
|
||||
'react/jsx-no-bind': 0,
|
||||
'import/order': 1,
|
||||
'no-multi-assign': 0,
|
||||
};
|
||||
|
||||
module.exports = getESLintConfig('react-ts', {
|
||||
|
|
@ -32,7 +33,9 @@ module.exports = getESLintConfig('react-ts', {
|
|||
'id-length': 0,
|
||||
'no-use-before-define': 0,
|
||||
'no-unused-vars': 0,
|
||||
'@typescript-eslint/no-unused-vars': 1,
|
||||
'@typescript-eslint/no-unused-vars': ['warn', {
|
||||
varsIgnorePattern: '[iI]gnored|createElement',
|
||||
}],
|
||||
'@typescript-eslint/ban-ts-ignore': 0,
|
||||
'@typescript-eslint/no-confusing-void-expression': 0,
|
||||
'@typescript-eslint/promise-function-async': 0,
|
||||
|
|
|
|||
|
|
@ -0,0 +1 @@
|
|||
chrome 55
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
import { defineConfig } from '@ice/app';
|
||||
|
||||
export default defineConfig({});
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
{
|
||||
"name": "app-config",
|
||||
"version": "1.0.0",
|
||||
"scripts": {
|
||||
"start": "ice start",
|
||||
"build": "ice build",
|
||||
"build:splitChunks": "ice build --config splitChunks.config.mts"
|
||||
},
|
||||
"description": "",
|
||||
"author": "",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@ice/app": "workspace:*",
|
||||
"@ice/plugin-auth": "workspace:*",
|
||||
"@ice/plugin-rax-compat": "workspace:*",
|
||||
"@ice/runtime": "workspace:*",
|
||||
"@uni/env": "^1.1.0",
|
||||
"ahooks": "^3.3.8",
|
||||
"react": "^18.0.0",
|
||||
"react-dom": "^18.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/react": "^18.0.0",
|
||||
"@types/react-dom": "^18.0.2",
|
||||
"speed-measure-webpack-plugin": "^1.5.0",
|
||||
"webpack": "^5.73.0"
|
||||
}
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 2.7 KiB |
|
|
@ -0,0 +1,13 @@
|
|||
import { defineAppConfig } from 'ice';
|
||||
|
||||
export default defineAppConfig({
|
||||
app: {
|
||||
rootId: 'app',
|
||||
strict: true,
|
||||
errorBoundary: true,
|
||||
},
|
||||
router: {
|
||||
type: 'browser',
|
||||
basename: '/ice',
|
||||
},
|
||||
});
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
import { Meta, Title, Links, Main, Scripts } from 'ice';
|
||||
|
||||
function Document() {
|
||||
return (
|
||||
<html>
|
||||
<head>
|
||||
<meta charSet="utf-8" />
|
||||
<meta name="description" content="ICE 3.0 Demo" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<Meta />
|
||||
<Title />
|
||||
<Links />
|
||||
</head>
|
||||
<body>
|
||||
<Main />
|
||||
<Scripts />
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
|
||||
export default Document;
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
body {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
export default function Error() {
|
||||
// @ts-ignore
|
||||
window.test();
|
||||
return <>error</>;
|
||||
}
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
export default function Home() {
|
||||
return <h1>home</h1>;
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
declare module '*.module.less' {
|
||||
const classes: { [key: string]: string };
|
||||
export default classes;
|
||||
}
|
||||
|
||||
declare module '*.module.css' {
|
||||
const classes: { [key: string]: string };
|
||||
export default classes;
|
||||
}
|
||||
|
||||
declare module '*.module.scss' {
|
||||
const classes: { [key: string]: string };
|
||||
export default classes;
|
||||
}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
{
|
||||
"compileOnSave": false,
|
||||
"buildOnSave": false,
|
||||
"compilerOptions": {
|
||||
"baseUrl": ".",
|
||||
"outDir": "build",
|
||||
"module": "esnext",
|
||||
"target": "es6",
|
||||
"jsx": "react-jsx",
|
||||
"moduleResolution": "node",
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"lib": ["es6", "dom"],
|
||||
"sourceMap": true,
|
||||
"allowJs": true,
|
||||
"rootDir": "./",
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"noImplicitReturns": true,
|
||||
"noImplicitThis": true,
|
||||
"noImplicitAny": false,
|
||||
"importHelpers": true,
|
||||
"strictNullChecks": true,
|
||||
"suppressImplicitAnyIndexErrors": true,
|
||||
"noUnusedLocals": true,
|
||||
"skipLibCheck": true,
|
||||
"paths": {
|
||||
"@/*": ["./src/*"],
|
||||
"ice": [".ice"]
|
||||
}
|
||||
},
|
||||
"include": ["src", ".ice", "ice.config.*"],
|
||||
"exclude": ["node_modules", "build", "public"]
|
||||
}
|
||||
|
|
@ -22,6 +22,14 @@ export default defineConfig({
|
|||
return webpackConfig;
|
||||
},
|
||||
dropLogLevel: 'warn',
|
||||
plugins: [auth(), custom],
|
||||
plugins: [
|
||||
auth(),
|
||||
{
|
||||
name: 'runtime-donot-exsist',
|
||||
setup() {},
|
||||
runtime: './test',
|
||||
},
|
||||
custom,
|
||||
],
|
||||
eslint: true,
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,6 +1,10 @@
|
|||
import style from './cssWithEscapedSymbols.module.css';
|
||||
|
||||
console.log('style', style);
|
||||
|
||||
export default function Bar() {
|
||||
return (
|
||||
<div>
|
||||
<div className={style.test}>
|
||||
bar
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,114 @@
|
|||
.test {
|
||||
background: red;
|
||||
}
|
||||
|
||||
._test {
|
||||
background: blue;
|
||||
}
|
||||
|
||||
.className {
|
||||
background: red;
|
||||
}
|
||||
|
||||
#someId {
|
||||
background: green;
|
||||
}
|
||||
|
||||
.className .subClass {
|
||||
color: green;
|
||||
}
|
||||
|
||||
#someId .subClass {
|
||||
color: blue;
|
||||
}
|
||||
|
||||
.-a0-34a___f {
|
||||
color: red;
|
||||
}
|
||||
|
||||
.m_x_\@ {
|
||||
margin-left: auto !important;
|
||||
margin-right: auto !important;
|
||||
}
|
||||
|
||||
.B\&W\? {
|
||||
margin-left: auto !important;
|
||||
margin-right: auto !important;
|
||||
}
|
||||
|
||||
/* matches elements with class=":`(" */
|
||||
.\3A \`\( {
|
||||
color: aqua;
|
||||
}
|
||||
|
||||
/* matches elements with class="1a2b3c" */
|
||||
.\31 a2b3c {
|
||||
color: aliceblue;
|
||||
}
|
||||
|
||||
/* matches the element with id="#fake-id" */
|
||||
#\#fake-id {
|
||||
color: antiquewhite;
|
||||
}
|
||||
|
||||
/* matches the element with id="-a-b-c-" */
|
||||
#-a-b-c- {
|
||||
color: azure;
|
||||
}
|
||||
|
||||
/* matches the element with id="©" */
|
||||
#© {
|
||||
color: black;
|
||||
}
|
||||
|
||||
.♥ { background: lime; }
|
||||
.© { background: lime; }
|
||||
.😍 { background: lime; }
|
||||
.“‘’” { background: lime; }
|
||||
.☺☃ { background: lime; }
|
||||
.⌘⌥ { background: lime; }
|
||||
.𝄞♪♩♫♬ { background: lime; }
|
||||
.💩 { background: lime; }
|
||||
.\? { background: lime; }
|
||||
.\@ { background: lime; }
|
||||
.\. { background: lime; }
|
||||
.\3A \) { background: lime; }
|
||||
.\3A \`\( { background: lime; }
|
||||
.\31 23 { background: lime; }
|
||||
.\31 a2b3c { background: lime; }
|
||||
.\<p\> { background: lime; }
|
||||
.\<\>\<\<\<\>\>\<\> { background: lime; }
|
||||
.\+\+\+\+\+\+\+\+\+\+\[\>\+\+\+\+\+\+\+\>\+\+\+\+\+\+\+\+\+\+\>\+\+\+\>\+\<\<\<\<\-\]\>\+\+\.\>\+\.\+\+\+\+\+\+\+\.\.\+\+\+\.\>\+\+\.\<\<\+\+\+\+\+\+\+\+\+\+\+\+\+\+\+\.\>\.\+\+\+\.\-\-\-\-\-\-\.\-\-\-\-\-\-\-\-\.\>\+\.\>\. { background: lime; }
|
||||
.\# { background: lime; }
|
||||
.\#\# { background: lime; }
|
||||
.\#\.\#\.\# { background: lime; }
|
||||
.\_ { background: lime; }
|
||||
.\{\} { background: lime; }
|
||||
.\#fake\-id { background: lime; }
|
||||
.foo\.bar { background: lime; }
|
||||
.\3A hover { background: lime; }
|
||||
.\3A hover\3A focus\3A active { background: lime; }
|
||||
.\[attr\=value\] { background: lime; }
|
||||
.f\/o\/o { background: lime; }
|
||||
.f\\o\\o { background: lime; }
|
||||
.f\*o\*o { background: lime; }
|
||||
.f\!o\!o { background: lime; }
|
||||
.f\'o\'o { background: lime; }
|
||||
.f\~o\~o { background: lime; }
|
||||
.f\+o\+o { background: lime; }
|
||||
|
||||
.foo\/bar {
|
||||
background: hotpink;
|
||||
}
|
||||
|
||||
.foo\\bar {
|
||||
background: hotpink;
|
||||
}
|
||||
|
||||
.foo\/bar\/baz {
|
||||
background: hotpink;
|
||||
}
|
||||
|
||||
.foo\\bar\\baz {
|
||||
background: hotpink;
|
||||
}
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
h2 {
|
||||
color: #000;
|
||||
}
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
.data {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
.data {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
|
@ -4,7 +4,10 @@ import { Link, useData, useAppData, useConfig } from 'ice';
|
|||
import { useAppContext } from '@ice/runtime';
|
||||
import { useRequest } from 'ahooks';
|
||||
import type { AppData } from 'ice';
|
||||
import './index.css';
|
||||
import styles from './index.module.css';
|
||||
import lessStyles from './index.module.less';
|
||||
import sassStyles from './index.module.scss';
|
||||
|
||||
const Bar = lazy(() => import('../components/bar'));
|
||||
|
||||
|
|
@ -33,8 +36,8 @@ export default function Home(props) {
|
|||
<Bar />
|
||||
</Suspense>
|
||||
<div className={styles.data}>
|
||||
<div>foo: {JSON.stringify(foo)}</div>
|
||||
<div>users: {JSON.stringify(users)}</div>
|
||||
<div className={lessStyles.data}>foo: {JSON.stringify(foo)}</div>
|
||||
<div className={sassStyles.data}>users: {JSON.stringify(users)}</div>
|
||||
<div>userInfo: {JSON.stringify(userInfo)}</div>
|
||||
<div>data from: <span id="data-from">{data.from}</span></div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -83,7 +83,7 @@
|
|||
"@types/temp": "^0.9.1",
|
||||
"chokidar": "^3.5.3",
|
||||
"react": "^18.2.0",
|
||||
"unplugin": "^0.8.0",
|
||||
"unplugin": "^0.9.0",
|
||||
"webpack": "^5.73.0",
|
||||
"webpack-dev-server": "^4.7.4"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -223,7 +223,7 @@ async function createService({ rootDir, command, commandArgs }: CreateServiceOpt
|
|||
reCompileRouteConfig,
|
||||
dataCache,
|
||||
appConfig,
|
||||
devPath: (routePaths[0] || '').replace(/^[\/\\]/, ''),
|
||||
devPath: (routePaths[0] || '').replace(/^[/\\]/, ''),
|
||||
spinner: buildSpinner,
|
||||
});
|
||||
} else if (command === 'build') {
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ import { less, sass, postcss } from '@ice/bundles';
|
|||
import type { Plugin, PluginBuild, OnResolveArgs, OnResolveResult, OnLoadArgs, OnLoadResult } from 'esbuild';
|
||||
|
||||
const cssModulesStyleFilter = /\.module\.(css|sass|scss|less)$/;
|
||||
const CSS_LOADER_NAMESPACE = 'css-loader-namespace';
|
||||
const STYLE_HANDLER_NAMESPACE = 'style-handler-namespace';
|
||||
|
||||
type GenerateScopedNameFunction = (name: string, filename: string, css: string) => string;
|
||||
|
|
@ -27,38 +26,20 @@ const cssModulesPlugin = (options: PluginOptions): Plugin => {
|
|||
build.onResolve({ filter: cssModulesStyleFilter }, onResolve);
|
||||
|
||||
build.onLoad({ filter: /.*/, namespace: STYLE_HANDLER_NAMESPACE }, onStyleLoad(options));
|
||||
|
||||
build.onLoad({ filter: /.*/, namespace: CSS_LOADER_NAMESPACE }, onCSSLoad);
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
async function onResolve(args: OnResolveArgs): Promise<OnResolveResult> {
|
||||
const { namespace, resolveDir } = args;
|
||||
const { resolveDir } = args;
|
||||
const absolutePath = path.resolve(resolveDir, args.path);
|
||||
// This is the import in the `STYLE_HANDLER_NAMESPACE` namespace.
|
||||
// Put the path in the `CSS_LOADER_NAMESPACE` namespace to tell esbuild to load the css file.
|
||||
if (namespace === STYLE_HANDLER_NAMESPACE) {
|
||||
return {
|
||||
path: absolutePath,
|
||||
namespace: CSS_LOADER_NAMESPACE,
|
||||
};
|
||||
}
|
||||
// Otherwise, generate css and put it in the `STYLE_HANDLER_NAMESPACE` namespace to handle css file
|
||||
// Generate css and put it in the `STYLE_HANDLER_NAMESPACE` namespace to handle css file
|
||||
return {
|
||||
path: absolutePath,
|
||||
namespace: STYLE_HANDLER_NAMESPACE,
|
||||
};
|
||||
}
|
||||
|
||||
async function onCSSLoad(args: OnLoadArgs): Promise<OnLoadResult> {
|
||||
const data = (await fse.readFile(args.path)).toString();
|
||||
return {
|
||||
contents: data,
|
||||
loader: 'css',
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* parse less/scss/css-modules to css
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ import { runClientApp, getAppConfig } from '@ice/runtime';
|
|||
import * as app from '@/app';
|
||||
import runtimeModules from './runtimeModules';
|
||||
import routes from './routes';
|
||||
import Document from '@/document';
|
||||
|
||||
const getRouterBasename = () => {
|
||||
const appConfig = getAppConfig(app);
|
||||
|
|
@ -13,7 +12,6 @@ runClientApp({
|
|||
app,
|
||||
runtimeModules,
|
||||
routes,
|
||||
Document,
|
||||
basename: getRouterBasename(),
|
||||
hydrate: <%- hydrate %>,
|
||||
memoryRouter: <%- memoryRouter || false %>,
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
import type { AppConfig } from 'ice';
|
||||
import { runApp } from 'ice';
|
||||
import { request, type Request } from 'ice';
|
||||
import page from '@/page';
|
||||
import url from './a.png';
|
||||
import page from '@/page';
|
||||
|
||||
console.log(url);
|
||||
runApp({
|
||||
request,
|
||||
page,
|
||||
} as AppConfig)
|
||||
} as AppConfig);
|
||||
|
||||
export default () => { };
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
var a = 1;
|
||||
let a = 1;
|
||||
if (true) {
|
||||
a = 2;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,10 +4,10 @@ import c from 'c';
|
|||
import d from 'd';
|
||||
|
||||
const [e, f, ...rest] = a;
|
||||
const {h, j} = b;
|
||||
const { h, j } = b;
|
||||
const [x, ...m] = c;
|
||||
const zz = 'x';
|
||||
const {k, l, ...s} = d;
|
||||
const { k, l, ...s } = d;
|
||||
|
||||
export function getConfig() {
|
||||
return {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
var j = 2;
|
||||
var i = 2;
|
||||
let j = 2;
|
||||
let i = 2;
|
||||
while (j < 3) {
|
||||
j++;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,71 @@
|
|||
import { expect, it, describe } from 'vitest';
|
||||
import formatWebpackMessages from '../src/utils/formatWebpackMessages';
|
||||
|
||||
describe('webpack message formatting', () => {
|
||||
it('format syntax error', () => {
|
||||
const result = formatWebpackMessages({
|
||||
errors: [{ message: 'Syntax error: Unterminated JSX contents (8:13)' }, { message: 'Module error' }],
|
||||
warnings: [],
|
||||
});
|
||||
expect(result.errors.length).toBe(1);
|
||||
});
|
||||
|
||||
it('formats aliased unknown export 1', () => {
|
||||
const result = formatWebpackMessages({
|
||||
errors: [{ message: 'export \'bar\' (imported as \'bar2\') was not found in \'./AppUnknownExport\'' }],
|
||||
warnings: [],
|
||||
});
|
||||
expect(result.errors[0]).toBe('Attempted import error: \'bar\' is not exported from \'./AppUnknownExport\' (imported as \'bar2\').');
|
||||
});
|
||||
|
||||
it('formats cannot find module sass', () => {
|
||||
const result = formatWebpackMessages({
|
||||
errors: [{ message: '\nCannot find module.import sass from \'sass\'' }],
|
||||
warnings: [],
|
||||
});
|
||||
expect(result.errors[0]).toBe('To import Sass files, you first need to install sass.\nRun `npm install sass` or `yarn add sass` inside your workspace.');
|
||||
});
|
||||
|
||||
it('formats module no found', () => {
|
||||
const result = formatWebpackMessages({
|
||||
errors: [{ message: '\nModule not found: Cannot find file: \'./ThisFileSouldNotExist\' in \'./src\'' }],
|
||||
warnings: [],
|
||||
});
|
||||
expect(result.errors[0]).toBe('Cannot find file: \'./ThisFileSouldNotExist\' in \'./src\'');
|
||||
});
|
||||
|
||||
it('remove leading newline', () => {
|
||||
const result = formatWebpackMessages({
|
||||
errors: [{ message: 'line1\n\n\n\nline3' }],
|
||||
warnings: [],
|
||||
});
|
||||
expect(result.errors[0]).toBe('line1\n\nline3');
|
||||
});
|
||||
|
||||
it('nested message', () => {
|
||||
const result = formatWebpackMessages({
|
||||
// @ts-ignore
|
||||
errors: [[{ message: 'line1' }, { message: 'line2\nline3' }]],
|
||||
warnings: [],
|
||||
});
|
||||
expect(result.errors[0]).toBe('line2\nline3');
|
||||
});
|
||||
|
||||
it('string message', () => {
|
||||
const result = formatWebpackMessages({
|
||||
// @ts-ignore
|
||||
errors: ['line2\nline3'],
|
||||
warnings: [],
|
||||
});
|
||||
expect(result.errors[0]).toBe('line2\nline3');
|
||||
});
|
||||
|
||||
it('eslint error', () => {
|
||||
const result = formatWebpackMessages({
|
||||
errors: [{ message: 'Line 4:13: Parsing error: \'b\' is not defined no-undef' }],
|
||||
warnings: [],
|
||||
});
|
||||
expect(result.errors[0]).toBe('Syntax error: \'b\' is not defined no-undef (4:13)');
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -9,7 +9,7 @@ describe('generateExports', () => {
|
|||
type: false,
|
||||
}]);
|
||||
expect(importStr).toBe('import Router from \'react-router\';');
|
||||
expect(exportStr).toBe('Router,')
|
||||
expect(exportStr).toBe('Router,');
|
||||
});
|
||||
it('type export', () => {
|
||||
const { importStr, exportStr } = generateExports([{
|
||||
|
|
@ -18,7 +18,7 @@ describe('generateExports', () => {
|
|||
type: true,
|
||||
}]);
|
||||
expect(importStr).toBe('import type Router from \'react-router\';');
|
||||
expect(exportStr).toBe('Router;')
|
||||
expect(exportStr).toBe('Router;');
|
||||
});
|
||||
it('named exports', () => {
|
||||
const { importStr, exportStr } = generateExports([{
|
||||
|
|
@ -34,7 +34,7 @@ describe('generateExports', () => {
|
|||
source: 'react-helmet',
|
||||
specifier: 'Helmet',
|
||||
exportAlias: {
|
||||
'Helmet': 'Head',
|
||||
Helmet: 'Head',
|
||||
},
|
||||
}]);
|
||||
expect(importStr).toBe('import Helmet from \'react-helmet\';');
|
||||
|
|
@ -52,40 +52,20 @@ const defaultExportData = [{
|
|||
|
||||
describe('checkExportData', () => {
|
||||
it('basic usage', () => {
|
||||
try {
|
||||
checkExportData(defaultExportData, { source: 'react-dom', specifier: 'react-dom' }, 'test-api');
|
||||
expect(true).toBe(true);
|
||||
} catch (err) {
|
||||
expect(true).toBe(false);
|
||||
}
|
||||
checkExportData(defaultExportData, { source: 'react-dom', specifier: 'react-dom' }, 'test-api');
|
||||
});
|
||||
|
||||
it('duplicate named export', () => {
|
||||
try {
|
||||
checkExportData(defaultExportData, defaultExportData[0], 'test-api');
|
||||
expect(true).toBe(false);
|
||||
} catch (err) {
|
||||
expect(true).toBe(true);
|
||||
}
|
||||
})
|
||||
expect(() => checkExportData(defaultExportData, defaultExportData[0], 'test-api')).toThrow();
|
||||
});
|
||||
|
||||
it('duplicate exports', () => {
|
||||
try {
|
||||
checkExportData(defaultExportData, defaultExportData, 'test-api');
|
||||
expect(true).toBe(false);
|
||||
} catch (err) {
|
||||
expect(true).toBe(true);
|
||||
}
|
||||
})
|
||||
expect(() => checkExportData(defaultExportData, defaultExportData, 'test-api')).toThrow();
|
||||
});
|
||||
|
||||
it('duplicate default export', () => {
|
||||
try {
|
||||
checkExportData(defaultExportData, { source: 'react-dom', specifier: 'Switch' }, 'test-api');
|
||||
expect(true).toBe(false);
|
||||
} catch (err) {
|
||||
expect(true).toBe(true);
|
||||
}
|
||||
})
|
||||
expect(() => checkExportData(defaultExportData, { source: 'react-dom', specifier: 'Switch' }, 'test-api')).toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
describe('removeExportData', () => {
|
||||
|
|
@ -102,4 +82,4 @@ describe('removeExportData', () => {
|
|||
const removed = removeExportData(defaultExportData, ['react-router', 'react-helmet']);
|
||||
expect(removed.length).toBe(0);
|
||||
});
|
||||
})
|
||||
});
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
import { expect, it, describe } from 'vitest';
|
||||
import openBrowser from '../src/utils/openBrowser';
|
||||
|
||||
describe('openBrowser in node', () => {
|
||||
it('open localhost', () => {
|
||||
process.env.BROWSER = 'none';
|
||||
const result = openBrowser('http://localhost/');
|
||||
expect(result).toBe(false);
|
||||
});
|
||||
|
||||
// TODO simulate open browser in node
|
||||
});
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
import { expect, it, describe } from 'vitest';
|
||||
import * as path from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
import { expect, it, describe } from 'vitest';
|
||||
import { analyzeImports, getImportPath, resolveId, type Alias } from '../src/service/analyze';
|
||||
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
|
|
@ -17,7 +17,7 @@ describe('resolveId', () => {
|
|||
expect(id).toBe('/.ice/runApp/test');
|
||||
});
|
||||
it('alias: { ice$: \'/.ice/runApp\'}; id: ice/test', () => {
|
||||
const alias = { 'ice$': '/.ice/runApp' };
|
||||
const alias = { ice$: '/.ice/runApp' };
|
||||
const id = resolveId('ice/test', alias);
|
||||
expect(id).toBe('ice/test');
|
||||
});
|
||||
|
|
@ -27,7 +27,7 @@ describe('resolveId', () => {
|
|||
expect(id).toBe(false);
|
||||
});
|
||||
it('alias: { foundnamejs: \'/user/folder\'}; id: foundnamejs', () => {
|
||||
const alias = { 'foundnamejs': '/user/folder' };
|
||||
const alias = { foundnamejs: '/user/folder' };
|
||||
const id = resolveId('foundnamejs', alias);
|
||||
expect(id).toBe('/user/folder');
|
||||
});
|
||||
|
|
@ -46,7 +46,7 @@ describe('getImportPath', () => {
|
|||
expect(filePath.replace(/^[A-Za-z]:/, '')).toBe('/rootDir/page.js');
|
||||
});
|
||||
it('import from alias', () => {
|
||||
const filePath = getImportPath('ice', '/rootDir/test.ts', { ice: '/rootDir/component.tsx'});
|
||||
const filePath = getImportPath('ice', '/rootDir/test.ts', { ice: '/rootDir/component.tsx' });
|
||||
expect(filePath).toBe('/rootDir/component.tsx');
|
||||
});
|
||||
it('import node_module dependency', () => {
|
||||
|
|
@ -57,7 +57,7 @@ describe('getImportPath', () => {
|
|||
|
||||
describe('analyzeImports', () => {
|
||||
it('basic usage', async () => {
|
||||
const entryFile = path.join(__dirname, './fixtures/preAnalyze/app.ts');
|
||||
const entryFile = path.join(__dirname, './fixtures/preAnalyze/app.ts');
|
||||
const analyzeSet = await analyzeImports([entryFile], {
|
||||
analyzeRelativeImport: true,
|
||||
alias: {
|
||||
|
|
@ -65,5 +65,5 @@ describe('analyzeImports', () => {
|
|||
},
|
||||
});
|
||||
expect([...(analyzeSet || [])]).toStrictEqual(['runApp', 'request', 'store']);
|
||||
})
|
||||
});
|
||||
});
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
import { afterAll, expect, it } from 'vitest';
|
||||
import * as path from 'path';
|
||||
import fse from 'fs-extra';
|
||||
import { fileURLToPath } from 'url';
|
||||
import { afterAll, expect, it } from 'vitest';
|
||||
import fse from 'fs-extra';
|
||||
import preBundleCJSDeps from '../src/service/preBundleCJSDeps';
|
||||
import { scanImports } from '../src/service/analyze';
|
||||
|
||||
|
|
@ -16,7 +16,7 @@ it('prebundle cjs deps', async () => {
|
|||
depsInfo: deps,
|
||||
cacheDir,
|
||||
alias,
|
||||
taskConfig: { mode: 'production' }
|
||||
taskConfig: { mode: 'production' },
|
||||
});
|
||||
|
||||
expect(fse.pathExistsSync(path.join(cacheDir, 'deps', 'react.js'))).toBeTruthy();
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import * as fs from 'fs';
|
|||
import { fileURLToPath } from 'url';
|
||||
import { expect, it, describe } from 'vitest';
|
||||
import { parse, type ParserOptions } from '@babel/parser';
|
||||
import traverse from '@babel/traverse'
|
||||
import traverse from '@babel/traverse';
|
||||
import generate from '@babel/generator';
|
||||
import removeTopLevelCodePlugin from '../src/utils/babelPluginRemoveCode';
|
||||
|
||||
|
|
@ -19,7 +19,7 @@ const parserOptions: ParserOptions = {
|
|||
'classPrivateMethods',
|
||||
'typescript',
|
||||
'decorators-legacy',
|
||||
]
|
||||
],
|
||||
};
|
||||
|
||||
describe('remove top level code', () => {
|
||||
|
|
@ -27,19 +27,19 @@ describe('remove top level code', () => {
|
|||
const ast = parse(fs.readFileSync(path.join(__dirname, './fixtures/removeCode/export-specifier.ts'), 'utf-8'), parserOptions);
|
||||
traverse(ast, removeTopLevelCodePlugin(['getConfig']));
|
||||
const content = generate(ast).code;
|
||||
expect(content.replace(/\n/g, '').replace(/\s+/g, ' ')).toBe(`const getConfig = () => {};export { getConfig };`);
|
||||
expect(content.replace(/\n/g, '').replace(/\s+/g, ' ')).toBe('const getConfig = () => {};export { getConfig };');
|
||||
});
|
||||
it('remove variable export', () => {
|
||||
const ast = parse(fs.readFileSync(path.join(__dirname, './fixtures/removeCode/export-variable.ts'), 'utf-8'), parserOptions);
|
||||
traverse(ast, removeTopLevelCodePlugin(['getConfig']));
|
||||
const content = generate(ast).code;
|
||||
expect(content.replace(/\n/g, '').replace(/\s+/g, ' ')).toBe(`export const getConfig = () => {};`);
|
||||
expect(content.replace(/\n/g, '').replace(/\s+/g, ' ')).toBe('export const getConfig = () => {};');
|
||||
});
|
||||
it('remove function export', () => {
|
||||
const ast = parse(fs.readFileSync(path.join(__dirname, './fixtures/removeCode/function-exports.ts'), 'utf-8'), parserOptions);
|
||||
traverse(ast, removeTopLevelCodePlugin(['getConfig']));
|
||||
const content = generate(ast).code;
|
||||
expect(content.replace(/\n/g, '').replace(/\s+/g, ' ')).toBe(`export function getConfig() {}`);
|
||||
expect(content.replace(/\n/g, '').replace(/\s+/g, ' ')).toBe('export function getConfig() {}');
|
||||
});
|
||||
it('remove if statement', () => {
|
||||
const ast = parse(fs.readFileSync(path.join(__dirname, './fixtures/removeCode/if.ts'), 'utf-8'), parserOptions);
|
||||
|
|
@ -51,7 +51,7 @@ describe('remove top level code', () => {
|
|||
const ast = parse(fs.readFileSync(path.join(__dirname, './fixtures/removeCode/import.ts'), 'utf-8'), parserOptions);
|
||||
traverse(ast, removeTopLevelCodePlugin(['getConfig']));
|
||||
const content = generate(ast).code;
|
||||
expect(content.replace(/\n/g, '').replace(/\s+/g, ' ')).toBe(`export function getConfig() { return { a: 1 };}`);
|
||||
expect(content.replace(/\n/g, '').replace(/\s+/g, ' ')).toBe('export function getConfig() { return { a: 1 };}');
|
||||
});
|
||||
it('remove IIFE code', () => {
|
||||
const ast = parse(fs.readFileSync(path.join(__dirname, './fixtures/removeCode/iife.ts'), 'utf-8'), parserOptions);
|
||||
|
|
@ -76,14 +76,14 @@ describe('remove top level code', () => {
|
|||
const ast = parse(fs.readFileSync(path.join(__dirname, './fixtures/removeCode/vars.ts'), 'utf-8'), parserOptions);
|
||||
traverse(ast, removeTopLevelCodePlugin(['getConfig']));
|
||||
const content = generate(ast).code;
|
||||
expect(content.replace(/\n/g, '').replace(/\s+/g, ' ')).toBe(`import c from 'c';import d from 'd';const [x] = c;const { k} = d;export function getConfig() { return { x, k };}`);
|
||||
expect(content.replace(/\n/g, '').replace(/\s+/g, ' ')).toBe('import c from \'c\';import d from \'d\';const [x] = c;const { k} = d;export function getConfig() { return { x, k };}');
|
||||
});
|
||||
|
||||
it('keep export default', () => {
|
||||
const ast = parse(fs.readFileSync(path.join(__dirname, './fixtures/removeCode/export-default.ts'), 'utf-8'), parserOptions);
|
||||
traverse(ast, removeTopLevelCodePlugin(['default']));
|
||||
const content = generate(ast).code;
|
||||
expect(content.replace(/\n/g, '').replace(/\s+/g, ' ')).toBe(`const a = 1;export default a;`);
|
||||
expect(content.replace(/\n/g, '').replace(/\s+/g, ' ')).toBe('const a = 1;export default a;');
|
||||
});
|
||||
|
||||
it('remove expression statement', () => {
|
||||
|
|
@ -92,4 +92,4 @@ describe('remove top level code', () => {
|
|||
const content = generate(ast).code;
|
||||
expect(content.replace(/\n/g, '').replace(/\s+/g, ' ')).toBe('');
|
||||
});
|
||||
})
|
||||
});
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
import { expect, it, describe } from 'vitest';
|
||||
import * as path from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
import { expect, it, describe } from 'vitest';
|
||||
import { scanImports } from '../src/service/analyze';
|
||||
import formatPath from '../src/utils/formatPath';
|
||||
|
||||
|
|
@ -30,7 +30,7 @@ describe('scan import', () => {
|
|||
it('scan with depImports', async () => {
|
||||
const deps = await scanImports(
|
||||
[path.join(__dirname, './fixtures/scan/app.ts')],
|
||||
{ alias, rootDir, depImports: { '@ice/runtime': { name: '@ice/runtime' }, react: { name: 'react' } } }
|
||||
{ alias, rootDir, depImports: { '@ice/runtime': { name: '@ice/runtime' }, react: { name: 'react' } } },
|
||||
);
|
||||
expect(deps['@ice/runtime'].name).toEqual('@ice/runtime');
|
||||
expect(deps['@ice/runtime'].pkgPath).toBeUndefined();
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
import { afterAll, expect, it } from 'vitest';
|
||||
import * as path from 'path';
|
||||
import fse from 'fs-extra';
|
||||
import { fileURLToPath } from 'url';
|
||||
import { afterAll, expect, it } from 'vitest';
|
||||
import fse from 'fs-extra';
|
||||
import esbuild from 'esbuild';
|
||||
import { createUnplugin } from 'unplugin';
|
||||
import preBundleCJSDeps from '../src/service/preBundleCJSDeps';
|
||||
import { scanImports } from '../src/service/analyze';
|
||||
import esbuild from 'esbuild';
|
||||
import transformImport from '../src/esbuild/transformImport';
|
||||
import aliasPlugin from '../src/esbuild/alias';
|
||||
import { createUnplugin } from 'unplugin';
|
||||
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
const alias = { '@': path.join(__dirname, './fixtures/scan') };
|
||||
|
|
@ -22,7 +22,7 @@ it('transform module import', async () => {
|
|||
depsInfo: deps,
|
||||
cacheDir,
|
||||
alias,
|
||||
taskConfig: { mode: 'production' }
|
||||
taskConfig: { mode: 'production' },
|
||||
});
|
||||
const transformImportPlugin = createUnplugin(() => transformImport(metadata, path.join(outdir, 'server'))).esbuild;
|
||||
await esbuild.build({
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ function isObject(obj: any): obj is object {
|
|||
}
|
||||
|
||||
// Support rpx unit.
|
||||
function hijackElementProps(props: { style?: object } | object): object {
|
||||
export function hijackElementProps(props: { style?: object } | object): object {
|
||||
if (props && STYLE in props) {
|
||||
const { style } = props;
|
||||
if (isObject(style)) {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,22 @@
|
|||
import { expect, it, describe } from 'vitest';
|
||||
import { hijackElementProps } from '../src/';
|
||||
|
||||
describe('hijack element', () => {
|
||||
it('hijackElementProps basic', () => {
|
||||
const props = hijackElementProps({ data: '', number: 1, fontSize: '12rpx' });
|
||||
expect(props).toStrictEqual({
|
||||
data: '', number: 1, fontSize: '12rpx',
|
||||
});
|
||||
});
|
||||
|
||||
it('hijackElementProps style', () => {
|
||||
const props = hijackElementProps({ style: { fontSize: 14, height: '12px', with: '12rpx' } });
|
||||
expect(props).toStrictEqual({
|
||||
style: {
|
||||
fontSize: 14,
|
||||
height: '12px',
|
||||
with: '1.6vw',
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
var a = 1;
|
||||
let a = 1;
|
||||
if (true) {
|
||||
a = 2;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,10 +4,10 @@ import c from 'c';
|
|||
import d from 'd';
|
||||
|
||||
const [e, f, ...rest] = a;
|
||||
const {h, j} = b;
|
||||
const { h, j } = b;
|
||||
const [x, ...m] = c;
|
||||
const zz = 'x';
|
||||
const {k, l, ...s} = d;
|
||||
const { k, l, ...s } = d;
|
||||
|
||||
export function getConfig() {
|
||||
return {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
var j = 2;
|
||||
var i = 2;
|
||||
let j = 2;
|
||||
let i = 2;
|
||||
while (j < 3) {
|
||||
j++;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -49,12 +49,12 @@ describe('rewrite app worker url', () => {
|
|||
appWorker: {
|
||||
url: 'pha-worker.js',
|
||||
source: 'test',
|
||||
}
|
||||
},
|
||||
})).toMatchObject({
|
||||
appWorker: {
|
||||
url: 'app-worker.js',
|
||||
source: 'test',
|
||||
}
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -62,7 +62,7 @@ describe('rewrite app worker url', () => {
|
|||
expect(rewriteAppWorker({})).toMatchObject({
|
||||
appWorker: {
|
||||
url: 'app-worker.js',
|
||||
}
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -78,11 +78,11 @@ describe('transform config keys', () => {
|
|||
{
|
||||
pageHeader: {
|
||||
includedSafeArea: true,
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{ isRoot: true }
|
||||
{ isRoot: true },
|
||||
);
|
||||
expect(manifestJSON.name).toStrictEqual('name');
|
||||
expect(manifestJSON.offline_resources).toStrictEqual(['//g.alicdn.com/.*']);
|
||||
|
|
@ -115,7 +115,7 @@ describe('transform config keys', () => {
|
|||
},
|
||||
],
|
||||
},
|
||||
{ isRoot: true }
|
||||
{ isRoot: true },
|
||||
);
|
||||
expect(manifestJSON?.data_prefetch?.length).toBe(1);
|
||||
expect(manifestJSON?.data_prefetch![0].data).toMatchObject({
|
||||
|
|
@ -154,11 +154,11 @@ describe('transform config keys', () => {
|
|||
text: 'text-name',
|
||||
icon: '',
|
||||
activeIcon: '',
|
||||
}
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{ isRoot: true }
|
||||
{ isRoot: true },
|
||||
);
|
||||
|
||||
expect(manifestJSON.tab_bar).toBeTruthy();
|
||||
|
|
@ -189,7 +189,7 @@ describe('transform config keys', () => {
|
|||
},
|
||||
],
|
||||
},
|
||||
{ isRoot: true }
|
||||
{ isRoot: true },
|
||||
);
|
||||
expect(manifestJSON?.pages?.length).toBe(2);
|
||||
expect(manifestJSON?.pages![0].data_prefetch).toMatchObject([
|
||||
|
|
@ -209,7 +209,7 @@ describe('transform config keys', () => {
|
|||
{
|
||||
a: 123,
|
||||
},
|
||||
{ isRoot: false }
|
||||
{ isRoot: false },
|
||||
);
|
||||
|
||||
expect(manifestJSON).toMatchObject({ a: 123 });
|
||||
|
|
@ -220,7 +220,7 @@ describe('transform config keys', () => {
|
|||
{
|
||||
a: 123,
|
||||
},
|
||||
{ isRoot: true }
|
||||
{ isRoot: true },
|
||||
);
|
||||
|
||||
expect(manifestJSON).toMatchObject({});
|
||||
|
|
@ -233,7 +233,7 @@ describe('transform config keys', () => {
|
|||
'U-Tag': '${storage.uTag}',
|
||||
},
|
||||
},
|
||||
{ isRoot: false }
|
||||
{ isRoot: false },
|
||||
);
|
||||
expect(manifestJSON).toMatchObject({ request_headers: { 'U-Tag': '${storage.uTag}' } });
|
||||
});
|
||||
|
|
@ -293,7 +293,7 @@ describe('parse manifest', async () => {
|
|||
'home',
|
||||
'about',
|
||||
'app/nest',
|
||||
'404'
|
||||
'404',
|
||||
],
|
||||
};
|
||||
const manifest = await parseManifest(phaManifest, {
|
||||
|
|
@ -403,7 +403,7 @@ describe('parse manifest', async () => {
|
|||
} catch (err) {
|
||||
expect(true).toBe(true);
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
it('url failed with new URL', async () => {
|
||||
const phaManifest = {
|
||||
|
|
@ -417,7 +417,7 @@ describe('parse manifest', async () => {
|
|||
});
|
||||
expect(manifest.tab_bar?.url).toBe('{{xxx}}/tabBar');
|
||||
expect(manifest.tab_bar?.name).toBe('{{xxx}}');
|
||||
})
|
||||
});
|
||||
|
||||
it('should not inject html when tabHeader & tabBar have url field', async () => {
|
||||
const phaManifest = {
|
||||
|
|
@ -426,14 +426,14 @@ describe('parse manifest', async () => {
|
|||
pageHeader: {
|
||||
source: 'pages/Header',
|
||||
url: 'https://m.taobao.com',
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
],
|
||||
tabBar: {
|
||||
custom: true,
|
||||
source: 'pages/CustomTabBar',
|
||||
items: ['home', 'frame1'],
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
const manifest = await parseManifest(phaManifest, {
|
||||
|
|
@ -477,8 +477,8 @@ describe('get multiple manifest', async () => {
|
|||
'app/nest',
|
||||
{
|
||||
url: 'https://m.taobao.com',
|
||||
}
|
||||
]
|
||||
},
|
||||
],
|
||||
},
|
||||
'about',
|
||||
],
|
||||
|
|
|
|||
|
|
@ -165,7 +165,7 @@ export default class IntersectionObserver {
|
|||
if (!Array.isArray(threshold)) threshold = [threshold];
|
||||
|
||||
return threshold.sort().filter((t, i, a) => {
|
||||
if (typeof t != 'number' || isNaN(t) || t < 0 || t > 1) {
|
||||
if (typeof t != 'number' || Number.isNaN(t) || t < 0 || t > 1) {
|
||||
throw new Error('threshold must be a number between 0 and 1 inclusively');
|
||||
}
|
||||
return t !== a[i - 1];
|
||||
|
|
@ -515,12 +515,12 @@ function throttle(fn, timeout) {
|
|||
* @param {Node} node The DOM node to add the event handler to.
|
||||
* @param {string} event The event name.
|
||||
* @param {Function} fn The event handler to add.
|
||||
* @param {boolean} opt_useCapture Optionally adds the even to the capture
|
||||
* @param {boolean} useCapture Optionally adds the even to the capture
|
||||
* phase. Note: this only works in modern browsers.
|
||||
*/
|
||||
function addEvent(node, event, fn, opt_useCapture) {
|
||||
function addEvent(node, event, fn, useCapture) {
|
||||
if (typeof node.addEventListener == 'function') {
|
||||
node.addEventListener(event, fn, opt_useCapture || false);
|
||||
node.addEventListener(event, fn, useCapture || false);
|
||||
} else if (typeof node.attachEvent == 'function') {
|
||||
node.attachEvent(`on${event}`, fn);
|
||||
}
|
||||
|
|
@ -532,12 +532,12 @@ function addEvent(node, event, fn, opt_useCapture) {
|
|||
* @param {Node} node The DOM node to remove the event handler from.
|
||||
* @param {string} event The event name.
|
||||
* @param {Function} fn The event handler to remove.
|
||||
* @param {boolean} opt_useCapture If the event handler was added with this
|
||||
* @param {boolean} useCapture If the event handler was added with this
|
||||
* flag set to true, it should be set to true here in order to remove it.
|
||||
*/
|
||||
function removeEvent(node, event, fn, opt_useCapture) {
|
||||
function removeEvent(node, event, fn, useCapture) {
|
||||
if (typeof node.removeEventListener == 'function') {
|
||||
node.removeEventListener(event, fn, opt_useCapture || false);
|
||||
node.removeEventListener(event, fn, useCapture || false);
|
||||
} else if (typeof node.detatchEvent == 'function') {
|
||||
node.detatchEvent(`on${event}`, fn);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
import type { ReactElement } from 'react';
|
||||
|
||||
type ChildrenElement<T> = ChildrenElement<T>[] | T;
|
||||
|
||||
// Mocked `Rax.shared`.
|
||||
const shared = {
|
||||
get Element(): any {
|
||||
|
|
@ -27,7 +29,7 @@ function warningCompat(message: string) {
|
|||
console.error(`[RaxCompat] ${stack}`);
|
||||
}
|
||||
|
||||
function flattenChildren(children: ReactElement) {
|
||||
function flattenChildren(children: ChildrenElement<ReactElement>) {
|
||||
if (children == null) {
|
||||
return children;
|
||||
}
|
||||
|
|
@ -39,7 +41,7 @@ function flattenChildren(children: ReactElement) {
|
|||
return result.length - 1 ? result : result[0];
|
||||
}
|
||||
|
||||
function traverseChildren(children: ReactElement, result: ReactElement[]) {
|
||||
function traverseChildren(children: ChildrenElement<ReactElement>, result: ReactElement[]) {
|
||||
if (Array.isArray(children)) {
|
||||
for (let i = 0, l = children.length; i < l; i++) {
|
||||
traverseChildren(children[i], result);
|
||||
|
|
|
|||
|
|
@ -72,7 +72,7 @@ function handleIntersect(entries: IntersectionObserverEntry[]) {
|
|||
} = entry;
|
||||
// No `top` value in polyfill.
|
||||
const currentY = boundingClientRect.y || boundingClientRect.top;
|
||||
const beforeY = parseInt(target.getAttribute('data-before-current-y')) || currentY;
|
||||
const beforeY = parseInt(target.getAttribute('data-before-current-y'), 10) || currentY;
|
||||
|
||||
// is in view
|
||||
if (
|
||||
|
|
|
|||
|
|
@ -1,25 +1,25 @@
|
|||
import React from 'react';
|
||||
import { expect, it, describe } from 'vitest';
|
||||
import Children from '../src/children';
|
||||
import { render } from '@testing-library/react';
|
||||
import Children from '../src/children';
|
||||
|
||||
const arrText = ['hello', 'rax'];
|
||||
|
||||
describe('children', () => {
|
||||
it('should work with map', () => {
|
||||
function Hello({ children }) {
|
||||
return <div data-testid="test">
|
||||
return (<div data-testid="test">
|
||||
{
|
||||
Children.map(children, (child, i) => {
|
||||
if (i === 0) {
|
||||
return <span>{arrText[1]}</span>
|
||||
return <span>{arrText[1]}</span>;
|
||||
}
|
||||
return child;
|
||||
})
|
||||
}
|
||||
</div>
|
||||
</div>);
|
||||
}
|
||||
|
||||
|
||||
const instance = (
|
||||
<Hello>
|
||||
<span>{arrText[0]}</span>
|
||||
|
|
@ -29,10 +29,10 @@ describe('children', () => {
|
|||
|
||||
const wrapper = render(instance);
|
||||
const node = wrapper.queryByTestId('test');
|
||||
|
||||
|
||||
expect(node.children.item(0).textContent).toBe(node.children.item(0).textContent);
|
||||
expect(node.children.item(1).textContent).toBe(arrText[1]);
|
||||
})
|
||||
});
|
||||
|
||||
it('should work with forEach', () => {
|
||||
function Hello({ children }) {
|
||||
|
|
@ -40,9 +40,9 @@ describe('children', () => {
|
|||
expect(child.type).toBe('span');
|
||||
expect(child.props.children).toBe(arrText[i]);
|
||||
});
|
||||
return children
|
||||
return children;
|
||||
}
|
||||
|
||||
|
||||
const instance = (
|
||||
<Hello>
|
||||
<span>{arrText[0]}</span>
|
||||
|
|
@ -51,14 +51,14 @@ describe('children', () => {
|
|||
);
|
||||
|
||||
render(instance);
|
||||
})
|
||||
});
|
||||
|
||||
it('should work with count', () => {
|
||||
function Hello({ children }) {
|
||||
expect(Children.count(children)).toBe(arrText.length);
|
||||
return children
|
||||
return children;
|
||||
}
|
||||
|
||||
|
||||
const instance = (
|
||||
<Hello>
|
||||
<span>{arrText[0]}</span>
|
||||
|
|
@ -67,15 +67,15 @@ describe('children', () => {
|
|||
);
|
||||
|
||||
render(instance);
|
||||
})
|
||||
});
|
||||
|
||||
it('should work with only', () => {
|
||||
let child = <span>{arrText[0]}</span>;
|
||||
function Hello({ children }) {
|
||||
expect(Children.only(children)).toBe(child);
|
||||
return children
|
||||
return children;
|
||||
}
|
||||
|
||||
|
||||
const instance = (
|
||||
<Hello>
|
||||
{
|
||||
|
|
@ -85,14 +85,14 @@ describe('children', () => {
|
|||
);
|
||||
|
||||
render(instance);
|
||||
})
|
||||
});
|
||||
|
||||
it('should work with toArray', () => {
|
||||
function Hello({ children }) {
|
||||
expect(Children.toArray(children).length).toBe(arrText.length);
|
||||
return children
|
||||
return children;
|
||||
}
|
||||
|
||||
|
||||
const instance = (
|
||||
<Hello>
|
||||
<span>{arrText[0]}</span>
|
||||
|
|
@ -101,7 +101,7 @@ describe('children', () => {
|
|||
);
|
||||
|
||||
render(instance);
|
||||
})
|
||||
});
|
||||
|
||||
it('should escape keys', () => {
|
||||
const zero = <div key="1" />;
|
||||
|
|
@ -139,7 +139,7 @@ describe('children', () => {
|
|||
null,
|
||||
<span key="y" />,
|
||||
kid,
|
||||
kid && React.cloneElement(kid, {key: 'z'}),
|
||||
kid && React.cloneElement(kid, { key: 'z' }),
|
||||
<hr />,
|
||||
],
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
import React from 'react';
|
||||
import { expect, it, describe } from 'vitest';
|
||||
import { render } from '@testing-library/react';
|
||||
import { Component } from '../src/index';
|
||||
import cloneElement from '../src/clone-element';
|
||||
import { render } from '@testing-library/react';
|
||||
|
||||
describe('cloneElement', () => {
|
||||
it('basic', () => {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import React from 'react';
|
||||
import { expect, it, describe } from 'vitest';
|
||||
import { Component, PureComponent, memo } from '../src/index';
|
||||
import { render } from '@testing-library/react';
|
||||
import { Component, PureComponent, memo } from '../src/index';
|
||||
|
||||
describe('component', () => {
|
||||
it('Component should work', () => {
|
||||
|
|
@ -31,8 +31,8 @@ describe('component', () => {
|
|||
});
|
||||
|
||||
it('memo should work', () => {
|
||||
const MyComponent = memo(function MyComponent(props: { text: string, id: string }) {
|
||||
return <div id={props.id}>{props.text}</div>
|
||||
const MyComponent = memo((props: { text: string; id: string }) => {
|
||||
return <div id={props.id}>{props.text}</div>;
|
||||
});
|
||||
|
||||
const wrapper = render(<MyComponent id="" text="memo demo" />);
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { expect, it, describe, vi } from 'vitest';
|
||||
import { createElement } from '../src/index';
|
||||
import { render } from '@testing-library/react';
|
||||
import { createElement } from '../src/index';
|
||||
|
||||
describe('createElement', () => {
|
||||
it('basic', () => {
|
||||
|
|
@ -8,7 +8,7 @@ describe('createElement', () => {
|
|||
const wrapper = render(createElement(
|
||||
'div',
|
||||
null,
|
||||
str
|
||||
str,
|
||||
));
|
||||
expect(wrapper.container.childNodes[0].textContent).toBe(str);
|
||||
});
|
||||
|
|
@ -17,32 +17,32 @@ describe('createElement', () => {
|
|||
let appearFun = vi.spyOn({
|
||||
func: () => {
|
||||
expect(appearFun).toHaveBeenCalled();
|
||||
}
|
||||
}, 'func')
|
||||
},
|
||||
}, 'func');
|
||||
const str = 'hello world';
|
||||
render(createElement(
|
||||
'div',
|
||||
{
|
||||
onAppear: appearFun
|
||||
onAppear: appearFun,
|
||||
},
|
||||
str
|
||||
str,
|
||||
));
|
||||
});
|
||||
|
||||
it('should work with onDisappear', () => {
|
||||
const func = () => {
|
||||
expect(disappearFun).toHaveBeenCalled();
|
||||
}
|
||||
};
|
||||
let disappearFun = vi.spyOn({
|
||||
func: func
|
||||
}, 'func')
|
||||
func: func,
|
||||
}, 'func');
|
||||
const str = 'hello world';
|
||||
render(createElement(
|
||||
'div',
|
||||
{
|
||||
onDisappear: func,
|
||||
},
|
||||
str
|
||||
str,
|
||||
));
|
||||
});
|
||||
|
||||
|
|
@ -53,10 +53,10 @@ describe('createElement', () => {
|
|||
{
|
||||
'data-testid': 'rpxTest',
|
||||
style: {
|
||||
width: '300rpx'
|
||||
}
|
||||
width: '300rpx',
|
||||
},
|
||||
},
|
||||
str
|
||||
str,
|
||||
));
|
||||
|
||||
const node = wrapper.queryByTestId('rpxTest');
|
||||
|
|
@ -84,7 +84,7 @@ describe('createElement', () => {
|
|||
{
|
||||
'data-testid': 'maxlengthTest',
|
||||
value: str,
|
||||
maxlength: ''
|
||||
maxlength: '',
|
||||
},
|
||||
));
|
||||
|
||||
|
|
|
|||
|
|
@ -1,23 +1,13 @@
|
|||
import { expect, it, describe } from 'vitest';
|
||||
import { Component } from '../src/index';
|
||||
import createFactory from '../src/create-factory';
|
||||
import { render } from '@testing-library/react';
|
||||
import createFactory from '../src/create-factory';
|
||||
|
||||
describe('createFactory', () => {
|
||||
it('basic', () => {
|
||||
class CustomComponent extends Component {
|
||||
text: string = '';
|
||||
|
||||
render() {
|
||||
return <div>{this.props.text}</div>;
|
||||
}
|
||||
}
|
||||
|
||||
const factory = createFactory("div");
|
||||
const div = factory(null,'div node');
|
||||
const factory = createFactory('div');
|
||||
const div = factory(null, 'div node');
|
||||
|
||||
const wrapper = render(div);
|
||||
const node = wrapper.queryByTestId('div');
|
||||
let res = wrapper.getAllByText('div node');
|
||||
|
||||
expect(res.length).toBe(1);
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
|
||||
import React from 'react';
|
||||
import { expect, it, describe } from 'vitest';
|
||||
import createPortal from '../src/create-portal';
|
||||
import { render } from '@testing-library/react';
|
||||
import createPortal from '../src/create-portal';
|
||||
|
||||
describe('createPortal', () => {
|
||||
it('basic', () => {
|
||||
|
|
@ -11,13 +11,13 @@ describe('createPortal', () => {
|
|||
const Portal = ({ children }) => {
|
||||
return createPortal(children, div);
|
||||
};
|
||||
|
||||
|
||||
function App() {
|
||||
return <div>
|
||||
return (<div>
|
||||
<Portal>
|
||||
<text>Hello Rax</text>
|
||||
</Portal>
|
||||
</div>
|
||||
</div>);
|
||||
}
|
||||
|
||||
render(<App />);
|
||||
|
|
|
|||
|
|
@ -1,16 +1,16 @@
|
|||
import { expect, it, describe } from 'vitest';
|
||||
import React from 'react';
|
||||
import createReactClass from '../src/create-class';
|
||||
import { render } from '@testing-library/react';
|
||||
import createReactClass from '../src/create-class';
|
||||
|
||||
describe('createReactClass', () => {
|
||||
it('basic', () => {
|
||||
const ReactClass = createReactClass({
|
||||
name: '',
|
||||
id: '',
|
||||
render: function() {
|
||||
render: function () {
|
||||
return <div data-testid={this.props.id}>Hello, {this.props.name}</div>;
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
const wrapper = render(<ReactClass id="reactClassId" name="raxCompat" />);
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
import { expect, it, describe, vi } from 'vitest';
|
||||
import React, { Children } from 'react';
|
||||
import React from 'react';
|
||||
import { render } from '@testing-library/react';
|
||||
import { useRef, useEffect } from '../src/index';
|
||||
import { createElement } from '../src/create-element';
|
||||
import findDOMNode from '../src/find-dom-node';
|
||||
import { render } from '@testing-library/react';
|
||||
import transformPrototypes from '../src/prototypes'
|
||||
import transformPrototypes from '../src/prototypes';
|
||||
|
||||
describe('events', () => {
|
||||
it('should work with onclick', () => {
|
||||
|
|
@ -13,20 +13,20 @@ describe('events', () => {
|
|||
|
||||
const obj = {
|
||||
handleClick: () => console.log('click'),
|
||||
}
|
||||
};
|
||||
|
||||
const click = vi.spyOn(obj, 'handleClick')
|
||||
const click = vi.spyOn(obj, 'handleClick');
|
||||
|
||||
useEffect(() => {
|
||||
const dom = findDOMNode(ref.current);
|
||||
dom.click();
|
||||
expect(click).toHaveBeenCalled()
|
||||
expect(click).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
return createElement('div', {
|
||||
onclick: obj.handleClick,
|
||||
ref: ref
|
||||
}, 'click me')
|
||||
ref: ref,
|
||||
}, 'click me');
|
||||
}
|
||||
|
||||
render(<App />);
|
||||
|
|
@ -34,82 +34,82 @@ describe('events', () => {
|
|||
|
||||
it('should work with ontouchstart', () => {
|
||||
expect(transformPrototypes({
|
||||
ontouchstart: () => { }
|
||||
ontouchstart: () => { },
|
||||
}).onTouchStart).toBeInstanceOf(Function);
|
||||
});
|
||||
|
||||
it('should work with onclick', () => {
|
||||
expect(transformPrototypes({
|
||||
onclick: () => { }
|
||||
onclick: () => { },
|
||||
}).onClick).toBeInstanceOf(Function);
|
||||
expect(transformPrototypes({
|
||||
onclick: () => { }
|
||||
onclick: () => { },
|
||||
}).onclick).toBe(undefined);
|
||||
});
|
||||
|
||||
it('should work with onClick', () => {
|
||||
expect(transformPrototypes({
|
||||
onClick: () => { }
|
||||
onClick: () => { },
|
||||
}).onClick).toBeInstanceOf(Function);
|
||||
});
|
||||
|
||||
it('should work with ondblclick', () => {
|
||||
expect(transformPrototypes({
|
||||
ondblclick: () => { }
|
||||
ondblclick: () => { },
|
||||
}).onDoubleClick).toBeInstanceOf(Function);
|
||||
expect(transformPrototypes({
|
||||
ondblclick: () => { }
|
||||
ondblclick: () => { },
|
||||
}).ondblclick).toBe(undefined);
|
||||
});
|
||||
|
||||
it('should work with onDblclick', () => {
|
||||
expect(transformPrototypes({
|
||||
onDblclick: () => { }
|
||||
onDblclick: () => { },
|
||||
}).onDoubleClick).toBeInstanceOf(Function);
|
||||
expect(transformPrototypes({
|
||||
onDblclick: () => { }
|
||||
onDblclick: () => { },
|
||||
}).onDblclick).toBe(undefined);
|
||||
});
|
||||
|
||||
it('should work with onDoubleClick', () => {
|
||||
expect(transformPrototypes({
|
||||
onDoubleClick: () => { }
|
||||
onDoubleClick: () => { },
|
||||
}).onDoubleClick).toBeInstanceOf(Function);
|
||||
});
|
||||
|
||||
it('should work with onmouseenter', () => {
|
||||
expect(transformPrototypes({
|
||||
onmouseenter: () => { }
|
||||
onmouseenter: () => { },
|
||||
}).onMouseEnter).toBeInstanceOf(Function);
|
||||
expect(transformPrototypes({
|
||||
onmouseenter: () => { }
|
||||
onmouseenter: () => { },
|
||||
}).onmouseenter).toBe(undefined);
|
||||
});
|
||||
|
||||
it('should work with onpointerenter', () => {
|
||||
expect(transformPrototypes({
|
||||
onpointerenter: () => { }
|
||||
onpointerenter: () => { },
|
||||
}).onPointerEnter).toBeInstanceOf(Function);
|
||||
expect(transformPrototypes({
|
||||
onpointerenter: () => { }
|
||||
onpointerenter: () => { },
|
||||
}).onpointerenter).toBe(undefined);
|
||||
});
|
||||
|
||||
it('should work with onchange', () => {
|
||||
expect(transformPrototypes({
|
||||
onchange: () => { }
|
||||
onchange: () => { },
|
||||
}).onChange).toBeInstanceOf(Function);
|
||||
expect(transformPrototypes({
|
||||
onchange: () => { }
|
||||
onchange: () => { },
|
||||
}).onchange).toBe(undefined);
|
||||
});
|
||||
|
||||
it('should work with onbeforeinput', () => {
|
||||
expect(transformPrototypes({
|
||||
onbeforeinput: () => { }
|
||||
onbeforeinput: () => { },
|
||||
}).onBeforeInput).toBeInstanceOf(Function);
|
||||
expect(transformPrototypes({
|
||||
onbeforeinput: () => { }
|
||||
onbeforeinput: () => { },
|
||||
}).onbeforeinput).toBe(undefined);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
import { expect, it, describe } from 'vitest';
|
||||
import React from 'react';
|
||||
import { render } from '@testing-library/react';
|
||||
import { useRef, useEffect } from '../src/index';
|
||||
import findDOMNode from '../src/find-dom-node';
|
||||
import { render } from '@testing-library/react';
|
||||
|
||||
describe('findDomNode', () => {
|
||||
it('basic', () => {
|
||||
|
|
|
|||
|
|
@ -1,15 +1,15 @@
|
|||
import { expect, it, describe } from 'vitest';
|
||||
import React from 'react';
|
||||
import Fragment from '../src/fragment';
|
||||
import { render } from '@testing-library/react';
|
||||
import Fragment from '../src/fragment';
|
||||
|
||||
describe('fragment', () => {
|
||||
it('basic', () => {
|
||||
function App() {
|
||||
return <Fragment>
|
||||
return (<Fragment>
|
||||
<header>A heading</header>
|
||||
<footer>A footer</footer>
|
||||
</Fragment>;
|
||||
</Fragment>);
|
||||
}
|
||||
|
||||
const wrapper = render(<App />);
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import { expect, it, describe, vi } from 'vitest';
|
||||
import React from 'react';
|
||||
import { render } from '@testing-library/react';
|
||||
import {
|
||||
useState,
|
||||
useEffect,
|
||||
|
|
@ -8,12 +9,11 @@ import {
|
|||
useContext,
|
||||
useRef,
|
||||
} from '../src/hooks';
|
||||
import { render } from '@testing-library/react';
|
||||
|
||||
describe('hooks', () => {
|
||||
it('useState', () => {
|
||||
function App() {
|
||||
const [state, setState] = useState({ text: 'text' });
|
||||
const [state] = useState({ text: 'text' });
|
||||
expect(state.text).toBe('text');
|
||||
return <div>{state.text}</div>;
|
||||
}
|
||||
|
|
@ -30,6 +30,8 @@ describe('hooks', () => {
|
|||
setLoading(false);
|
||||
expect(loading).toBe(false);
|
||||
}, 1);
|
||||
// Expect useEffect to execute once
|
||||
// eslint-disable-next-line
|
||||
}, []);
|
||||
return <div>{loading ? 'loading...' : 'load end'}</div>;
|
||||
}
|
||||
|
|
@ -41,11 +43,11 @@ describe('hooks', () => {
|
|||
let useEffectFunc = vi.spyOn({
|
||||
func: () => {
|
||||
expect(useEffectFunc).toHaveBeenCalled();
|
||||
}
|
||||
}, 'func')
|
||||
},
|
||||
}, 'func');
|
||||
function App() {
|
||||
useEffect(useEffectFunc, []);
|
||||
|
||||
|
||||
return <div>useEffect</div>;
|
||||
}
|
||||
|
||||
|
|
@ -56,11 +58,11 @@ describe('hooks', () => {
|
|||
let useEffectFunc = vi.spyOn({
|
||||
func: () => {
|
||||
expect(useEffectFunc).toHaveBeenCalled();
|
||||
}
|
||||
}, 'func')
|
||||
},
|
||||
}, 'func');
|
||||
function App() {
|
||||
useLayoutEffect(useEffectFunc, []);
|
||||
|
||||
|
||||
return <div>useEffect</div>;
|
||||
}
|
||||
|
||||
|
|
@ -69,7 +71,7 @@ describe('hooks', () => {
|
|||
|
||||
it('useContext', () => {
|
||||
const Context = createContext({
|
||||
theme: 'dark'
|
||||
theme: 'dark',
|
||||
});
|
||||
function App() {
|
||||
const context = useContext(Context);
|
||||
|
|
@ -79,7 +81,7 @@ describe('hooks', () => {
|
|||
const wrapper = render(<App />);
|
||||
wrapper.findAllByText('dark').then((res) => {
|
||||
expect(res.length).toBe(1);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('useRef', () => {
|
||||
|
|
@ -87,15 +89,14 @@ describe('hooks', () => {
|
|||
const inputEl = useRef(null);
|
||||
useEffect(() => {
|
||||
expect(inputEl.current).instanceOf(Element);
|
||||
})
|
||||
});
|
||||
return (
|
||||
<>
|
||||
<input ref={inputEl} type="text" />
|
||||
</>
|
||||
<>
|
||||
<input ref={inputEl} type="text" />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
render(<TextInputWithFocusButton />);
|
||||
});
|
||||
|
||||
});
|
||||
|
|
|
|||
|
|
@ -5,9 +5,9 @@ import isValidElement from '../src/is-valid-element';
|
|||
describe('isValidElement', () => {
|
||||
it('basic', () => {
|
||||
function App() {
|
||||
return <div>
|
||||
return (<div>
|
||||
<div>isValidElement</div>
|
||||
</div>;
|
||||
</div>);
|
||||
}
|
||||
|
||||
expect(isValidElement(<App />)).toBe(true);
|
||||
|
|
|
|||
|
|
@ -4,10 +4,24 @@ import { shared } from '../src/index';
|
|||
|
||||
describe('render', () => {
|
||||
it('basic', () => {
|
||||
console.log('shared.Element', shared)
|
||||
console.log('shared.Element', shared);
|
||||
expect(shared.Element).toBe(null);
|
||||
expect(shared.Host).toBe(null);
|
||||
expect(shared.Instance).toBe(null);
|
||||
expect(shared.flattenChildren).instanceOf(Function);
|
||||
});
|
||||
|
||||
it('flattenChildren null', () => {
|
||||
// @ts-ignore e
|
||||
expect(shared.flattenChildren(null)).toBe(null);
|
||||
});
|
||||
|
||||
it('flattenChildren common', () => {
|
||||
expect(shared.flattenChildren(<>div</>)).toStrictEqual(<React.Fragment>div</React.Fragment>);
|
||||
});
|
||||
|
||||
it('flattenChildren array', () => {
|
||||
const children = [[[<>div</>]]];
|
||||
expect(shared.flattenChildren(children)).toStrictEqual(<React.Fragment>div</React.Fragment>);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { expect, test, describe } from 'vitest';
|
||||
import path from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
import { expect, test, describe } from 'vitest';
|
||||
import { generateRouteManifest, formatNestedRouteManifest } from '../src/index';
|
||||
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { expect, test, describe } from 'vitest';
|
||||
import path from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
import { expect, test, describe } from 'vitest';
|
||||
import { generateRouteManifest } from '../src/index';
|
||||
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
|
|
@ -33,7 +33,7 @@ describe('generateRouteManifest function', () => {
|
|||
});
|
||||
|
||||
test('invalid-routes', () => {
|
||||
expect(() => generateRouteManifest(path.join(fixturesDir, 'invalid-routes'))).toThrow(`invalid character in 'src/pages/[a.pdf].tsx'. Only support char: -, \\w, /`);
|
||||
expect(() => generateRouteManifest(path.join(fixturesDir, 'invalid-routes'))).toThrow('invalid character in \'src/pages/[a.pdf].tsx\'. Only support char: -, \\w, /');
|
||||
});
|
||||
|
||||
test('ignore-routes', () => {
|
||||
|
|
@ -43,11 +43,11 @@ describe('generateRouteManifest function', () => {
|
|||
|
||||
test('define-extra-routes', () => {
|
||||
const routeManifest = generateRouteManifest(
|
||||
path.join(fixturesDir, 'basic-routes'),
|
||||
['About/index.tsx'],
|
||||
path.join(fixturesDir, 'basic-routes'),
|
||||
['About/index.tsx'],
|
||||
(defineRoute) => {
|
||||
defineRoute('/about-me', 'About/index.tsx');
|
||||
}
|
||||
},
|
||||
);
|
||||
expect(routeManifest).toMatchSnapshot();
|
||||
});
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@ export function Links(props) {
|
|||
<>
|
||||
{
|
||||
routeLinks.map(link => {
|
||||
const { block, ...routeLinkProps } = link;
|
||||
const { block: ignored, ...routeLinkProps } = link;
|
||||
return <link key={link.href} {...props} {...routeLinkProps} data-route-link />;
|
||||
})
|
||||
}
|
||||
|
|
@ -104,7 +104,7 @@ export function Scripts(props) {
|
|||
/>
|
||||
{
|
||||
routeScripts.map(script => {
|
||||
const { block, ...routeScriptProps } = script;
|
||||
const { block: ignored, ...routeScriptProps } = script;
|
||||
return <script key={script.src} {...props} {...routeScriptProps} data-route-script />;
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,11 +18,11 @@ export default function getAppConfig(appExport: AppExport): AppConfig {
|
|||
return {
|
||||
app: {
|
||||
...defaultAppConfig.app,
|
||||
...(appConfig.app || {}),
|
||||
...(app || {}),
|
||||
},
|
||||
router: {
|
||||
...defaultAppConfig.router,
|
||||
...(appConfig.router || {}),
|
||||
...(router || {}),
|
||||
},
|
||||
...others,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -15,7 +15,6 @@ export default function matchRoutes(
|
|||
const matchRoutesFn = process.env.ICE_CORE_ROUTER === 'true' ? originMatchRoutes : matchRoutesSingle;
|
||||
let matches = matchRoutesFn(routes as unknown as RouteObject[], location, basename);
|
||||
if (!matches) return [];
|
||||
|
||||
return matches.map(({ params, pathname, pathnameBase, route }) => ({
|
||||
params,
|
||||
pathname,
|
||||
|
|
|
|||
|
|
@ -147,7 +147,7 @@ export function createRouteElements(
|
|||
});
|
||||
}
|
||||
|
||||
function RouteComponent({ id }: { id: string }) {
|
||||
export function RouteComponent({ id }: { id: string }) {
|
||||
// get current route component from latest routeModules
|
||||
const { routeModules } = useAppContext();
|
||||
const { default: Component } = routeModules[id] || {};
|
||||
|
|
@ -169,7 +169,6 @@ export function filterMatchesToLoad(prevMatches: RouteMatch[], currentMatches: R
|
|||
let isNew = (match: RouteMatch, index: number) => {
|
||||
// [a] -> [a, b]
|
||||
if (!prevMatches[index]) return true;
|
||||
|
||||
// [a, b] -> [a, c]
|
||||
return match.route.id !== prevMatches[index].route.id;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import { createHashHistory, createBrowserHistory, createMemoryHistory } from 'hi
|
|||
import type { HashHistory, BrowserHistory, Action, Location, InitialEntry, MemoryHistory } from 'history';
|
||||
import type {
|
||||
AppContext, AppExport, RouteItem, AppRouterProps, RoutesData, RoutesConfig,
|
||||
RouteWrapperConfig, RuntimeModules, RouteMatch, RouteModules, AppConfig, DocumentComponent,
|
||||
RouteWrapperConfig, RuntimeModules, RouteMatch, RouteModules, AppConfig,
|
||||
} from '@ice/types';
|
||||
import { createHistory as createHistorySingle } from './single-router.js';
|
||||
import { setHistory } from './history.js';
|
||||
|
|
@ -22,7 +22,6 @@ interface RunClientAppOptions {
|
|||
app: AppExport;
|
||||
routes: RouteItem[];
|
||||
runtimeModules: RuntimeModules;
|
||||
Document: DocumentComponent;
|
||||
hydrate: boolean;
|
||||
basename?: string;
|
||||
memoryRouter?: boolean;
|
||||
|
|
@ -35,7 +34,6 @@ export default async function runClientApp(options: RunClientAppOptions) {
|
|||
app,
|
||||
routes,
|
||||
runtimeModules,
|
||||
Document,
|
||||
basename,
|
||||
hydrate,
|
||||
memoryRouter,
|
||||
|
|
@ -71,7 +69,7 @@ export default async function runClientApp(options: RunClientAppOptions) {
|
|||
routesData = await loadRoutesData(matches, requestContext, routeModules);
|
||||
}
|
||||
if (!routesConfig) {
|
||||
routesConfig = getRoutesConfig(matches, routesConfig, routeModules);
|
||||
routesConfig = getRoutesConfig(matches, routesData, routeModules);
|
||||
}
|
||||
|
||||
const appContext: AppContext = {
|
||||
|
|
@ -98,15 +96,14 @@ export default async function runClientApp(options: RunClientAppOptions) {
|
|||
|
||||
await Promise.all(runtimeModules.map(m => runtime.loadModule(m)).filter(Boolean));
|
||||
|
||||
render({ runtime, Document, history });
|
||||
render({ runtime, history });
|
||||
}
|
||||
|
||||
interface RenderOptions {
|
||||
history: History;
|
||||
runtime: Runtime;
|
||||
Document: DocumentComponent;
|
||||
}
|
||||
async function render({ history, runtime, Document }: RenderOptions) {
|
||||
async function render({ history, runtime }: RenderOptions) {
|
||||
const appContext = runtime.getAppContext();
|
||||
const { appConfig } = appContext;
|
||||
const render = runtime.getRender();
|
||||
|
|
@ -122,7 +119,6 @@ async function render({ history, runtime, Document }: RenderOptions) {
|
|||
AppProvider={AppProvider}
|
||||
RouteWrappers={RouteWrappers}
|
||||
AppRouter={AppRouter}
|
||||
Document={Document}
|
||||
/>,
|
||||
);
|
||||
}
|
||||
|
|
@ -133,7 +129,6 @@ interface BrowserEntryProps {
|
|||
AppProvider: React.ComponentType<any>;
|
||||
RouteWrappers: RouteWrapperConfig[];
|
||||
AppRouter: React.ComponentType<AppRouterProps>;
|
||||
Document: DocumentComponent;
|
||||
}
|
||||
|
||||
interface HistoryState {
|
||||
|
|
@ -151,7 +146,6 @@ interface RouteState {
|
|||
function BrowserEntry({
|
||||
history,
|
||||
appContext,
|
||||
Document,
|
||||
...rest
|
||||
}: BrowserEntryProps) {
|
||||
const {
|
||||
|
|
@ -231,7 +225,7 @@ function BrowserEntry({
|
|||
* Prepare for the next pages.
|
||||
* Load modules、getPageData and preLoad the custom assets.
|
||||
*/
|
||||
async function loadNextPage(
|
||||
export async function loadNextPage(
|
||||
currentMatches: RouteMatch[],
|
||||
preRouteState: RouteState,
|
||||
) {
|
||||
|
|
|
|||
|
|
@ -1,13 +1,33 @@
|
|||
import { expect, test, describe } from 'vitest';
|
||||
import { defineAppConfig } from '../src/appConfig.js';
|
||||
import { expect, it, describe } from 'vitest';
|
||||
import getAppConfig, { defineAppConfig } from '../src/appConfig.js';
|
||||
|
||||
describe('AppConfig', () => {
|
||||
test('defineAppConfig', () => {
|
||||
it('getAppConfig', () => {
|
||||
const appConfig = getAppConfig({
|
||||
default: {
|
||||
app: {
|
||||
rootId: 'app',
|
||||
},
|
||||
},
|
||||
auth: {},
|
||||
});
|
||||
expect(appConfig).toStrictEqual({
|
||||
app: {
|
||||
rootId: 'app',
|
||||
strict: false,
|
||||
},
|
||||
router: {
|
||||
type: 'browser',
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('defineAppConfig', () => {
|
||||
const appConfig = {};
|
||||
expect(defineAppConfig(appConfig)).toEqual(appConfig);
|
||||
});
|
||||
|
||||
test('defineAppConfig fn', () => {
|
||||
it('defineAppConfig fn', () => {
|
||||
const appConfig = {};
|
||||
expect(defineAppConfig(() => appConfig)).toEqual(appConfig);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -0,0 +1,33 @@
|
|||
import { expect, it, describe } from 'vitest';
|
||||
import { createStaticNavigator } from '../src/server/navigator';
|
||||
|
||||
describe('mock server navigator', () => {
|
||||
const staticNavigator = createStaticNavigator();
|
||||
it('createHref', () => {
|
||||
expect(staticNavigator.createHref('/')).toBe('/');
|
||||
});
|
||||
|
||||
it('push', () => {
|
||||
expect(() => staticNavigator.push('/')).toThrow();
|
||||
});
|
||||
|
||||
it('replace', () => {
|
||||
expect(() => staticNavigator.replace('/')).toThrow();
|
||||
});
|
||||
|
||||
it('go', () => {
|
||||
expect(() => staticNavigator.go(1)).toThrow();
|
||||
});
|
||||
|
||||
it('back', () => {
|
||||
expect(() => staticNavigator.back()).toThrow();
|
||||
});
|
||||
|
||||
it('forward', () => {
|
||||
expect(() => staticNavigator.forward()).toThrow();
|
||||
});
|
||||
|
||||
it('block', () => {
|
||||
expect(() => staticNavigator.block()).toThrow();
|
||||
});
|
||||
});
|
||||
|
|
@ -1,82 +0,0 @@
|
|||
import { expect, test, describe } from 'vitest';
|
||||
import { filterMatchesToLoad } from '../src/routes.js';
|
||||
|
||||
describe('routes', () => {
|
||||
test('filter new matches', () => {
|
||||
const oldMatches = [
|
||||
{
|
||||
pathname: '/',
|
||||
route: {
|
||||
id: '/page/layout'
|
||||
}
|
||||
},
|
||||
{
|
||||
pathname: '/',
|
||||
route: {
|
||||
id: '/page/home'
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
const newMatches = [
|
||||
{
|
||||
pathname: '/',
|
||||
route: {
|
||||
id: '/page/layout'
|
||||
}
|
||||
},
|
||||
{
|
||||
pathname: '/about',
|
||||
route: {
|
||||
id: '/page/about'
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
// @ts-ignore
|
||||
const matches = filterMatchesToLoad(oldMatches, newMatches);
|
||||
|
||||
expect(
|
||||
matches
|
||||
).toEqual([{
|
||||
pathname: '/about',
|
||||
route: {
|
||||
id: '/page/about'
|
||||
}
|
||||
}]);
|
||||
});
|
||||
|
||||
test('filter matches with path changed', () => {
|
||||
const oldMatches = [
|
||||
{
|
||||
pathname: '/users/123',
|
||||
route: {
|
||||
id: '/users/123'
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
const newMatches = [
|
||||
{
|
||||
pathname: '/users/456',
|
||||
route: {
|
||||
id: '/users/456'
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
// @ts-ignore
|
||||
const matches = filterMatchesToLoad(oldMatches, newMatches);
|
||||
|
||||
expect(
|
||||
matches
|
||||
).toEqual([
|
||||
{
|
||||
pathname: '/users/456',
|
||||
route: {
|
||||
id: '/users/456'
|
||||
}
|
||||
}
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,322 @@
|
|||
import React from 'react';
|
||||
import { renderToString } from 'react-dom/server';
|
||||
import { expect, it, describe, beforeEach, afterEach, vi } from 'vitest';
|
||||
import type { RouteComponent as IRouteComponent } from '@ice/types/esm/runtime';
|
||||
import RouteWrapper from '../src/RouteWrapper';
|
||||
import { AppContextProvider } from '../src/AppContext';
|
||||
import {
|
||||
filterMatchesToLoad,
|
||||
createRouteElements,
|
||||
RouteComponent,
|
||||
loadRouteModules,
|
||||
loadRoutesData,
|
||||
getRoutesConfig,
|
||||
} from '../src/routes.js';
|
||||
|
||||
describe('routes', () => {
|
||||
let windowSpy;
|
||||
beforeEach(() => {
|
||||
windowSpy = vi.spyOn(global, 'window', 'get');
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
windowSpy.mockRestore();
|
||||
});
|
||||
|
||||
Object.defineProperty(window, 'open', open);
|
||||
const homeItem = {
|
||||
default: () => <></>,
|
||||
getConfig: () => ({ title: 'home' }),
|
||||
getData: async () => ({ type: 'getData' }),
|
||||
getServerData: async () => ({ type: 'getServerData' }),
|
||||
getStaticData: async () => ({ type: 'getStaticData' }),
|
||||
};
|
||||
const aboutItem = {
|
||||
default: () => <></>,
|
||||
getConfig: () => ({ title: 'about' }),
|
||||
};
|
||||
const routeModules = [
|
||||
{
|
||||
id: 'home',
|
||||
load: async () => {
|
||||
return homeItem as IRouteComponent;
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'about',
|
||||
load: async () => {
|
||||
return aboutItem as IRouteComponent;
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
it('route Component', () => {
|
||||
const domstring = renderToString(
|
||||
// @ts-ignore
|
||||
<AppContextProvider value={{ routeModules: {
|
||||
home: {
|
||||
default: () => <div>home</div>,
|
||||
},
|
||||
} }}
|
||||
>
|
||||
<RouteComponent id="home" />
|
||||
</AppContextProvider>,
|
||||
);
|
||||
expect(domstring).toBe('<div>home</div>');
|
||||
});
|
||||
|
||||
it('route error', () => {
|
||||
const currentEnv = process.env.NODE_ENV;
|
||||
process.env.NODE_ENV = 'development';
|
||||
expect(() => renderToString(
|
||||
// @ts-ignore
|
||||
<AppContextProvider value={{ routeModules: {} }}>
|
||||
<RouteComponent id="home" />
|
||||
</AppContextProvider>,
|
||||
)).toThrow();
|
||||
process.env.NODE_ENV = currentEnv;
|
||||
});
|
||||
|
||||
it('load route modules', async () => {
|
||||
windowSpy.mockImplementation(() => ({}));
|
||||
const routeModule = await loadRouteModules(routeModules, { home: homeItem });
|
||||
expect(routeModule).toStrictEqual({
|
||||
home: homeItem,
|
||||
about: aboutItem,
|
||||
});
|
||||
});
|
||||
|
||||
it('load error module', async () => {
|
||||
const routeModule = await loadRouteModules([{
|
||||
id: 'error',
|
||||
// @ts-ignore
|
||||
load: async () => {
|
||||
throw new Error('err');
|
||||
return {};
|
||||
},
|
||||
}], {});
|
||||
expect(routeModule).toStrictEqual({
|
||||
error: undefined,
|
||||
});
|
||||
});
|
||||
|
||||
it('load route data', async () => {
|
||||
const routeModule = await loadRouteModules(routeModules);
|
||||
const routesDataSSG = await loadRoutesData(
|
||||
// @ts-ignore
|
||||
[{ route: routeModules[0] }],
|
||||
{},
|
||||
routeModule,
|
||||
'SSG',
|
||||
);
|
||||
const routesDataSSR = await loadRoutesData(
|
||||
// @ts-ignore
|
||||
[{ route: routeModules[0] }],
|
||||
{},
|
||||
routeModule,
|
||||
'SSR',
|
||||
);
|
||||
const routesDataCSR = await loadRoutesData(
|
||||
// @ts-ignore
|
||||
[{ route: routeModules[0] }],
|
||||
{},
|
||||
routeModule,
|
||||
'CSR',
|
||||
);
|
||||
expect(routesDataSSG).toStrictEqual({
|
||||
home: {
|
||||
type: 'getStaticData',
|
||||
},
|
||||
});
|
||||
expect(routesDataSSR).toStrictEqual({
|
||||
home: {
|
||||
type: 'getServerData',
|
||||
},
|
||||
});
|
||||
expect(routesDataCSR).toStrictEqual({
|
||||
home: {
|
||||
type: 'getData',
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('load data from __ICE_DATA_LOADER__', async () => {
|
||||
windowSpy.mockImplementation(() => ({
|
||||
__ICE_DATA_LOADER__: async (id) => ({ id: `${id}_data` }),
|
||||
}));
|
||||
const routesData = await loadRoutesData(
|
||||
// @ts-ignore
|
||||
[{ route: routeModules[0] }],
|
||||
{},
|
||||
{},
|
||||
'SSG',
|
||||
);
|
||||
expect(routesData).toStrictEqual({
|
||||
home: {
|
||||
id: 'home_data',
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('get routes config', async () => {
|
||||
const routeModule = await loadRouteModules(routeModules);
|
||||
const routesConfig = getRoutesConfig(
|
||||
// @ts-ignore
|
||||
[{ route: routeModules[0] }],
|
||||
{ home: {} },
|
||||
routeModule,
|
||||
);
|
||||
expect(routesConfig).toStrictEqual({
|
||||
home: {
|
||||
title: 'home',
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('get routes config when failed get route module', async () => {
|
||||
const routesConfig = getRoutesConfig(
|
||||
// @ts-ignore
|
||||
[{ route: routeModules[0] }],
|
||||
{ home: {} },
|
||||
{},
|
||||
);
|
||||
expect(routesConfig).toStrictEqual({
|
||||
home: {},
|
||||
});
|
||||
});
|
||||
|
||||
it('create route element', () => {
|
||||
const routeElement = createRouteElements([{
|
||||
path: '/',
|
||||
id: 'home',
|
||||
componentName: 'home',
|
||||
}]);
|
||||
expect(routeElement).toEqual([{
|
||||
componentName: 'home',
|
||||
element: (
|
||||
<RouteWrapper id="home">
|
||||
<RouteComponent
|
||||
id="home"
|
||||
/>
|
||||
</RouteWrapper>
|
||||
),
|
||||
id: 'home',
|
||||
path: '/',
|
||||
}]);
|
||||
});
|
||||
|
||||
it('create route with children', () => {
|
||||
const routeElement = createRouteElements([{
|
||||
path: '/',
|
||||
id: 'home',
|
||||
componentName: 'home',
|
||||
children: [{
|
||||
path: '/about',
|
||||
id: 'about',
|
||||
componentName: 'about',
|
||||
}],
|
||||
}]);
|
||||
expect(routeElement).toEqual([{
|
||||
componentName: 'home',
|
||||
element: (
|
||||
<RouteWrapper id="home">
|
||||
<RouteComponent
|
||||
id="home"
|
||||
/>
|
||||
</RouteWrapper>
|
||||
),
|
||||
children: [{
|
||||
componentName: 'about',
|
||||
element: (
|
||||
<RouteWrapper id="about">
|
||||
<RouteComponent
|
||||
id="about"
|
||||
/>
|
||||
</RouteWrapper>
|
||||
),
|
||||
id: 'about',
|
||||
path: '/about',
|
||||
}],
|
||||
id: 'home',
|
||||
path: '/',
|
||||
}]);
|
||||
});
|
||||
|
||||
it('filter new matches', () => {
|
||||
const oldMatches = [
|
||||
{
|
||||
pathname: '/',
|
||||
route: {
|
||||
id: '/page/layout',
|
||||
},
|
||||
},
|
||||
{
|
||||
pathname: '/',
|
||||
route: {
|
||||
id: '/page/home',
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const newMatches = [
|
||||
{
|
||||
pathname: '/',
|
||||
route: {
|
||||
id: '/page/layout',
|
||||
},
|
||||
},
|
||||
{
|
||||
pathname: '/about',
|
||||
route: {
|
||||
id: '/page/about',
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
// @ts-ignore
|
||||
const matches = filterMatchesToLoad(oldMatches, newMatches);
|
||||
|
||||
expect(
|
||||
matches,
|
||||
).toEqual([{
|
||||
pathname: '/about',
|
||||
route: {
|
||||
id: '/page/about',
|
||||
},
|
||||
}]);
|
||||
});
|
||||
|
||||
it('filter matches with path changed', () => {
|
||||
const oldMatches = [
|
||||
{
|
||||
pathname: '/users/123',
|
||||
route: {
|
||||
id: '/users/123',
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const newMatches = [
|
||||
{
|
||||
pathname: '/users/456',
|
||||
route: {
|
||||
id: '/users/456',
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
// @ts-ignore
|
||||
const matches = filterMatchesToLoad(oldMatches, newMatches);
|
||||
|
||||
expect(
|
||||
matches,
|
||||
).toEqual([
|
||||
{
|
||||
pathname: '/users/456',
|
||||
route: {
|
||||
id: '/users/456',
|
||||
},
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,69 @@
|
|||
import { expect, it, vi, describe, beforeEach, afterEach } from 'vitest';
|
||||
|
||||
import { updateRoutesConfig } from '../src/routesConfig';
|
||||
|
||||
describe('routes config', () => {
|
||||
let documentSpy;
|
||||
const insertTags: any[] = [];
|
||||
const appendTags: any[] = [];
|
||||
|
||||
beforeEach(() => {
|
||||
documentSpy = vi.spyOn(global, 'document', 'get');
|
||||
documentSpy.mockImplementation(() => ({
|
||||
head: {
|
||||
querySelector: () => ({
|
||||
content: '',
|
||||
}),
|
||||
insertBefore: (tag) => {
|
||||
insertTags.push(tag);
|
||||
},
|
||||
appendChild: (tag) => {
|
||||
appendTags.push(tag);
|
||||
tag.onload();
|
||||
},
|
||||
},
|
||||
getElementById: () => null,
|
||||
querySelectorAll: () => [],
|
||||
createElement: (type) => {
|
||||
const element = {
|
||||
type,
|
||||
setAttribute: (attr, value) => {
|
||||
element[attr] = value;
|
||||
},
|
||||
};
|
||||
return element;
|
||||
},
|
||||
}));
|
||||
});
|
||||
afterEach(() => {
|
||||
documentSpy.mockRestore();
|
||||
});
|
||||
|
||||
it('update routes config', async () => {
|
||||
const routesConfig = {
|
||||
home: {
|
||||
title: 'home',
|
||||
meta: [
|
||||
{
|
||||
name: 'theme-color',
|
||||
content: '#eee',
|
||||
},
|
||||
],
|
||||
links: [{
|
||||
href: 'https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css',
|
||||
rel: 'stylesheet',
|
||||
}],
|
||||
scripts: [{
|
||||
src: 'https://cdn.jsdelivr.net/npm/lodash@2.4.1/dist/lodash.min.js',
|
||||
}],
|
||||
},
|
||||
};
|
||||
// @ts-ignore
|
||||
await updateRoutesConfig([{ route: { id: 'home' } }], routesConfig);
|
||||
expect(insertTags.length).toBe(1);
|
||||
expect(insertTags[0]?.type).toBe('meta');
|
||||
expect(appendTags.length).toBe(2);
|
||||
expect(appendTags[0]?.type).toBe('link');
|
||||
expect(appendTags[1]?.type).toBe('script');
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,350 @@
|
|||
import React from 'react';
|
||||
import { renderToString } from 'react-dom/server';
|
||||
import { expect, it, vi, describe, beforeEach, afterEach } from 'vitest';
|
||||
import runClientApp, { loadNextPage } from '../src/runClientApp';
|
||||
import { useAppData } from '../src/AppData';
|
||||
import { useConfig, useData } from '../src/RouteContext';
|
||||
|
||||
describe('run client app', () => {
|
||||
let windowSpy;
|
||||
let documentSpy;
|
||||
const mockData = {
|
||||
location: new URL('http://localhost:4000/'),
|
||||
history: {
|
||||
replaceState: vi.fn(),
|
||||
},
|
||||
addEventListener: vi.fn(),
|
||||
};
|
||||
beforeEach(() => {
|
||||
process.env.ICE_CORE_ROUTER = 'true';
|
||||
windowSpy = vi.spyOn(global, 'window', 'get');
|
||||
documentSpy = vi.spyOn(global, 'document', 'get');
|
||||
|
||||
windowSpy.mockImplementation(() => mockData);
|
||||
documentSpy.mockImplementation(() => ({
|
||||
head: {
|
||||
querySelector: () => ({
|
||||
content: '',
|
||||
}),
|
||||
},
|
||||
getElementById: () => null,
|
||||
querySelectorAll: () => [],
|
||||
}));
|
||||
});
|
||||
afterEach(() => {
|
||||
windowSpy.mockRestore();
|
||||
documentSpy.mockRestore();
|
||||
});
|
||||
|
||||
let domstring = '';
|
||||
|
||||
const serverRuntime = async ({ setRender }) => {
|
||||
setRender((container, element) => {
|
||||
try {
|
||||
domstring = renderToString(element as any);
|
||||
} catch (err) {}
|
||||
});
|
||||
};
|
||||
|
||||
const wrapperRuntime = async ({ addWrapper }) => {
|
||||
const RouteWrapper = ({ children }) => {
|
||||
return <div>{children}</div>;
|
||||
};
|
||||
addWrapper(RouteWrapper, true);
|
||||
};
|
||||
|
||||
const providerRuntmie = async ({ addProvider }) => {
|
||||
const Provider = ({ children }) => {
|
||||
return <div>{children}</div>;
|
||||
};
|
||||
addProvider(Provider);
|
||||
// Add twice.
|
||||
addProvider(Provider);
|
||||
};
|
||||
|
||||
const basicRoutes = [
|
||||
{
|
||||
id: 'home',
|
||||
path: '/',
|
||||
componentName: 'Home',
|
||||
load: async () => ({
|
||||
default: () => {
|
||||
// eslint-disable-next-line react-hooks/rules-of-hooks
|
||||
const appData = useAppData();
|
||||
return (
|
||||
<div>home{appData?.msg || ''}</div>
|
||||
);
|
||||
},
|
||||
getConfig: () => ({ title: 'home' }),
|
||||
getData: async () => ({ data: 'test' }),
|
||||
}),
|
||||
},
|
||||
];
|
||||
|
||||
it('run client basic', async () => {
|
||||
windowSpy.mockImplementation(() => ({
|
||||
...mockData,
|
||||
location: new URL('http://localhost:4000/?test=1&runtime=true&baisc'),
|
||||
}));
|
||||
|
||||
await runClientApp({
|
||||
app: {},
|
||||
routes: basicRoutes,
|
||||
runtimeModules: [serverRuntime],
|
||||
hydrate: false,
|
||||
});
|
||||
expect(domstring).toBe('<div>home</div>');
|
||||
});
|
||||
|
||||
it('run client single-router', async () => {
|
||||
process.env.ICE_CORE_ROUTER = '';
|
||||
await runClientApp({
|
||||
app: {},
|
||||
routes: basicRoutes,
|
||||
runtimeModules: [serverRuntime],
|
||||
hydrate: false,
|
||||
});
|
||||
process.env.ICE_CORE_ROUTER = 'true';
|
||||
expect(domstring).toBe('<div>home</div>');
|
||||
});
|
||||
|
||||
it('run client with wrapper', async () => {
|
||||
await runClientApp({
|
||||
app: {},
|
||||
routes: basicRoutes,
|
||||
runtimeModules: [serverRuntime, wrapperRuntime],
|
||||
hydrate: true,
|
||||
});
|
||||
expect(domstring).toBe('<div><div>home</div></div>');
|
||||
});
|
||||
|
||||
it('run client with app provider', async () => {
|
||||
await runClientApp({
|
||||
app: {},
|
||||
routes: basicRoutes,
|
||||
runtimeModules: [serverRuntime, providerRuntmie],
|
||||
hydrate: true,
|
||||
});
|
||||
expect(domstring).toBe('<div><div><div>home</div></div></div>');
|
||||
});
|
||||
|
||||
it('run client with empty route', async () => {
|
||||
await runClientApp({
|
||||
app: {},
|
||||
routes: [],
|
||||
runtimeModules: [serverRuntime],
|
||||
hydrate: false,
|
||||
});
|
||||
});
|
||||
|
||||
it('run client with memory router', async () => {
|
||||
const routes = [...basicRoutes, {
|
||||
id: 'about',
|
||||
path: '/about',
|
||||
componentName: 'About',
|
||||
load: async () => ({
|
||||
default: () => {
|
||||
return (
|
||||
<div>about</div>
|
||||
);
|
||||
},
|
||||
}),
|
||||
}];
|
||||
await runClientApp({
|
||||
app: {
|
||||
default: {
|
||||
router: {
|
||||
type: 'memory',
|
||||
initialEntries: ['/about'],
|
||||
},
|
||||
},
|
||||
},
|
||||
routes,
|
||||
runtimeModules: [serverRuntime],
|
||||
hydrate: true,
|
||||
});
|
||||
|
||||
expect(domstring).toBe('<div>about</div>');
|
||||
await runClientApp({
|
||||
app: {
|
||||
default: {
|
||||
},
|
||||
},
|
||||
routes,
|
||||
runtimeModules: [serverRuntime],
|
||||
hydrate: true,
|
||||
});
|
||||
});
|
||||
|
||||
it('run client with memory router - from context', async () => {
|
||||
windowSpy.mockImplementation(() => ({
|
||||
...mockData,
|
||||
__ICE_APP_CONTEXT__: {
|
||||
routePath: '/about',
|
||||
},
|
||||
}));
|
||||
const routes = [...basicRoutes, {
|
||||
id: 'about',
|
||||
path: '/about',
|
||||
componentName: 'About',
|
||||
load: async () => ({
|
||||
default: () => {
|
||||
return (
|
||||
<div>about</div>
|
||||
);
|
||||
},
|
||||
}),
|
||||
}];
|
||||
await runClientApp({
|
||||
app: {
|
||||
},
|
||||
routes,
|
||||
runtimeModules: [serverRuntime],
|
||||
hydrate: true,
|
||||
memoryRouter: true,
|
||||
});
|
||||
|
||||
expect(domstring).toBe('<div>about</div>');
|
||||
});
|
||||
|
||||
it('run client with hash router', async () => {
|
||||
await runClientApp({
|
||||
app: {
|
||||
default: {
|
||||
router: {
|
||||
type: 'hash',
|
||||
},
|
||||
},
|
||||
},
|
||||
routes: basicRoutes,
|
||||
runtimeModules: [serverRuntime],
|
||||
hydrate: true,
|
||||
});
|
||||
expect(domstring).toBe('<div>home</div>');
|
||||
});
|
||||
|
||||
it('run client with app data', async () => {
|
||||
let executed = false;
|
||||
await runClientApp({
|
||||
app: {
|
||||
getAppData: async () => {
|
||||
executed = true;
|
||||
return { msg: '-getAppData' };
|
||||
},
|
||||
},
|
||||
routes: basicRoutes,
|
||||
runtimeModules: [serverRuntime],
|
||||
hydrate: false,
|
||||
});
|
||||
expect(domstring).toBe('<div>home<!-- -->-getAppData</div>');
|
||||
expect(executed).toBe(true);
|
||||
});
|
||||
|
||||
it('run client with app data', async () => {
|
||||
let useGlobalLoader = false;
|
||||
let executed = false;
|
||||
windowSpy.mockImplementation(() => ({
|
||||
...mockData,
|
||||
__ICE_DATA_LOADER__: async () => {
|
||||
useGlobalLoader = true;
|
||||
return { msg: '-globalData' };
|
||||
},
|
||||
}));
|
||||
|
||||
await runClientApp({
|
||||
app: {
|
||||
getAppData: async () => {
|
||||
executed = true;
|
||||
return { msg: 'app' };
|
||||
},
|
||||
},
|
||||
routes: basicRoutes,
|
||||
runtimeModules: [serverRuntime],
|
||||
hydrate: false,
|
||||
});
|
||||
expect(executed).toBe(false);
|
||||
expect(useGlobalLoader).toBe(true);
|
||||
expect(domstring).toBe('<div>home<!-- -->-globalData</div>');
|
||||
});
|
||||
|
||||
it('run client with AppErrorBoundary', async () => {
|
||||
await runClientApp({
|
||||
app: {
|
||||
default: {
|
||||
app: {
|
||||
errorBoundary: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
routes: [{
|
||||
id: 'home',
|
||||
path: '/',
|
||||
componentName: 'Home',
|
||||
load: async () => ({
|
||||
default: () => {
|
||||
// eslint-disable-next-line react-hooks/rules-of-hooks
|
||||
const config = useConfig();
|
||||
// eslint-disable-next-line react-hooks/rules-of-hooks
|
||||
const data = useData();
|
||||
return (
|
||||
<div>home{data?.data}{config.title}</div>
|
||||
);
|
||||
},
|
||||
getConfig: () => ({ title: 'home' }),
|
||||
getData: async () => ({ data: 'test' }),
|
||||
}),
|
||||
}],
|
||||
runtimeModules: [serverRuntime],
|
||||
hydrate: false,
|
||||
});
|
||||
expect(domstring).toBe('<div>home<!-- -->test<!-- -->home</div>');
|
||||
});
|
||||
|
||||
it('load next page', async () => {
|
||||
const homePage = {
|
||||
default: () => <></>,
|
||||
getConfig: () => ({ title: 'home' }),
|
||||
getData: async () => ({ type: 'getDataHome' }),
|
||||
};
|
||||
const aboutPage = {
|
||||
default: () => <></>,
|
||||
getConfig: () => ({ title: 'about' }),
|
||||
getData: async () => ({ type: 'getDataAbout' }),
|
||||
};
|
||||
const mockedModules = [
|
||||
{
|
||||
id: 'home',
|
||||
load: async () => {
|
||||
return homePage;
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'about',
|
||||
load: async () => {
|
||||
return aboutPage;
|
||||
},
|
||||
},
|
||||
];
|
||||
const { routesData, routesConfig, routeModules } = await loadNextPage(
|
||||
// @ts-ignore
|
||||
[{ route: mockedModules[0] }],
|
||||
{
|
||||
// @ts-ignore
|
||||
matches: [{ route: mockedModules[1] }],
|
||||
routesData: {},
|
||||
routeModules: {},
|
||||
},
|
||||
);
|
||||
expect(routesData).toStrictEqual({
|
||||
home: { type: 'getDataHome' },
|
||||
});
|
||||
expect(routesConfig).toStrictEqual({
|
||||
home: {
|
||||
title: 'home',
|
||||
},
|
||||
});
|
||||
expect(routeModules).toStrictEqual({
|
||||
home: homePage,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,212 @@
|
|||
import React from 'react';
|
||||
import { expect, it, describe } from 'vitest';
|
||||
import { renderToHTML, renderToResponse } from '../src/runServerApp';
|
||||
import { Meta, Title, Links, Main, Scripts } from '../src/Document';
|
||||
|
||||
describe('run server app', () => {
|
||||
process.env.ICE_CORE_ROUTER = 'true';
|
||||
const basicRoutes = [
|
||||
{
|
||||
id: 'home',
|
||||
path: 'home',
|
||||
componentName: 'Home',
|
||||
load: async () => ({
|
||||
default: () => {
|
||||
return (
|
||||
<div>home</div>
|
||||
);
|
||||
},
|
||||
getConfig: () => ({ title: 'home' }),
|
||||
getData: async () => ({ data: 'test' }),
|
||||
}),
|
||||
},
|
||||
];
|
||||
|
||||
const assetsManifest = {
|
||||
publicPath: '/',
|
||||
assets: {},
|
||||
entries: [],
|
||||
pages: {
|
||||
home: ['js/home.js'],
|
||||
},
|
||||
};
|
||||
|
||||
const Document = () => (
|
||||
<html>
|
||||
<head>
|
||||
<meta charSet="utf-8" />
|
||||
<meta name="description" content="ICE 3.0 Demo" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<Meta />
|
||||
<Title />
|
||||
<Links />
|
||||
</head>
|
||||
<body>
|
||||
<Main />
|
||||
<Scripts />
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
|
||||
it('render to html', async () => {
|
||||
const html = await renderToHTML({
|
||||
// @ts-ignore
|
||||
req: {
|
||||
url: '/home',
|
||||
},
|
||||
}, {
|
||||
app: {},
|
||||
assetsManifest,
|
||||
runtimeModules: [],
|
||||
routes: basicRoutes,
|
||||
Document,
|
||||
renderMode: 'SSR',
|
||||
});
|
||||
// @ts-ignore
|
||||
expect(html?.value?.includes('<div>home</div>')).toBe(true);
|
||||
// @ts-ignore
|
||||
expect(html?.value?.includes('js/home.js')).toBe(true);
|
||||
});
|
||||
|
||||
it('render to html basename', async () => {
|
||||
const html = await renderToHTML({
|
||||
// @ts-ignore
|
||||
req: {
|
||||
url: '/home',
|
||||
},
|
||||
}, {
|
||||
app: {},
|
||||
assetsManifest,
|
||||
runtimeModules: [],
|
||||
routes: basicRoutes,
|
||||
Document,
|
||||
renderMode: 'SSR',
|
||||
basename: '/ice',
|
||||
});
|
||||
// @ts-ignore
|
||||
expect(html?.statusCode).toBe(404);
|
||||
});
|
||||
|
||||
it('render to html serverOnlyBasename', async () => {
|
||||
const html = await renderToHTML({
|
||||
// @ts-ignore
|
||||
req: {
|
||||
url: '/home',
|
||||
},
|
||||
}, {
|
||||
app: {},
|
||||
assetsManifest,
|
||||
runtimeModules: [],
|
||||
routes: basicRoutes,
|
||||
Document,
|
||||
renderMode: 'SSR',
|
||||
serverOnlyBasename: '/',
|
||||
basename: '/ice',
|
||||
});
|
||||
// @ts-ignore
|
||||
expect(html?.statusCode).toBe(200);
|
||||
});
|
||||
|
||||
it('render to 404 html', async () => {
|
||||
const html = await renderToHTML({
|
||||
// @ts-ignore
|
||||
req: {
|
||||
url: '/about',
|
||||
},
|
||||
}, {
|
||||
app: {},
|
||||
assetsManifest,
|
||||
runtimeModules: [],
|
||||
routes: basicRoutes,
|
||||
Document,
|
||||
});
|
||||
// @ts-ignore
|
||||
expect(html?.statusCode).toBe(404);
|
||||
});
|
||||
|
||||
it('router hash', async () => {
|
||||
const html = await renderToHTML({
|
||||
// @ts-ignore
|
||||
req: {
|
||||
url: '/home',
|
||||
},
|
||||
}, {
|
||||
app: {
|
||||
default: {
|
||||
router: {
|
||||
type: 'hash',
|
||||
},
|
||||
},
|
||||
},
|
||||
assetsManifest,
|
||||
runtimeModules: [],
|
||||
routes: basicRoutes,
|
||||
Document,
|
||||
});
|
||||
// @ts-ignore
|
||||
expect(html?.statusCode).toBe(200);
|
||||
// @ts-ignore
|
||||
expect(html?.value?.includes('<div>home</div>')).toBe(false);
|
||||
});
|
||||
|
||||
it('fallback to csr', async () => {
|
||||
const html = await renderToHTML({
|
||||
// @ts-ignore
|
||||
req: {
|
||||
url: '/home',
|
||||
},
|
||||
}, {
|
||||
app: {},
|
||||
assetsManifest,
|
||||
runtimeModules: [],
|
||||
routes: [{
|
||||
id: 'home',
|
||||
path: 'home',
|
||||
componentName: 'Home',
|
||||
load: async () => ({
|
||||
default: () => {
|
||||
throw new Error('err');
|
||||
return (
|
||||
<div>home</div>
|
||||
);
|
||||
},
|
||||
}),
|
||||
}],
|
||||
Document,
|
||||
});
|
||||
// @ts-ignore
|
||||
expect(html?.value?.includes('<div>home</div>')).toBe(false);
|
||||
// @ts-ignore
|
||||
expect(html?.value?.includes('js/home.js')).toBe(true);
|
||||
});
|
||||
|
||||
it('render to response', async () => {
|
||||
let htmlContent = '';
|
||||
await renderToResponse({
|
||||
// @ts-ignore
|
||||
req: {
|
||||
url: '/home',
|
||||
},
|
||||
res: {
|
||||
destination: {},
|
||||
// @ts-ignore
|
||||
setHeader: () => {},
|
||||
// @ts-ignore
|
||||
end: (content) => {
|
||||
htmlContent = content;
|
||||
},
|
||||
},
|
||||
}, {
|
||||
app: {},
|
||||
assetsManifest,
|
||||
runtimeModules: [],
|
||||
routes: basicRoutes,
|
||||
Document,
|
||||
renderMode: 'SSR',
|
||||
routePath: '/',
|
||||
documentOnly: true,
|
||||
});
|
||||
expect(!!htmlContent).toBe(true);
|
||||
expect(htmlContent.includes('<div>home</div')).toBe(false);
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
import React from 'react';
|
||||
import { expect, it, describe } from 'vitest';
|
||||
|
||||
import {
|
||||
useRoutes,
|
||||
Router,
|
||||
createHistory,
|
||||
matchRoutes,
|
||||
Link,
|
||||
Outlet,
|
||||
useParams,
|
||||
useSearchParams,
|
||||
useLocation,
|
||||
useNavigate,
|
||||
} from '../src/single-router';
|
||||
|
||||
describe('single route api', () => {
|
||||
it('useRoutes', () => {
|
||||
expect(useRoutes([{ element: <div>test</div> }])).toStrictEqual(
|
||||
<React.Fragment>
|
||||
<div>
|
||||
test
|
||||
</div>
|
||||
</React.Fragment>);
|
||||
});
|
||||
|
||||
it('Router', () => {
|
||||
expect(Router({ children: <div>test</div> })).toStrictEqual(
|
||||
<React.Fragment>
|
||||
<div>
|
||||
test
|
||||
</div>
|
||||
</React.Fragment>);
|
||||
});
|
||||
|
||||
it('createHistory', () => {
|
||||
expect(createHistory().location).toBe('');
|
||||
});
|
||||
|
||||
it('matchRoutes', () => {
|
||||
expect(matchRoutes([{}])[0].pathname).toBe('');
|
||||
});
|
||||
|
||||
it('Link', () => {
|
||||
expect(Link()).toBe(null);
|
||||
});
|
||||
|
||||
it('Outlet', () => {
|
||||
expect(Outlet()).toStrictEqual(<React.Fragment />);
|
||||
});
|
||||
|
||||
it('useParams', () => {
|
||||
expect(useParams()).toStrictEqual({});
|
||||
});
|
||||
|
||||
it('useSearchParams', () => {
|
||||
expect(useSearchParams()[0]).toStrictEqual({});
|
||||
});
|
||||
|
||||
it('useLocation', () => {
|
||||
expect(useLocation()).toStrictEqual({});
|
||||
});
|
||||
|
||||
it('useNavigate', () => {
|
||||
expect(useNavigate()).toStrictEqual({});
|
||||
});
|
||||
});
|
||||
|
|
@ -3,58 +3,58 @@ import { importStyle } from '../src/index';
|
|||
|
||||
describe('import style', () => {
|
||||
it('simple import', async () => {
|
||||
const sourceCode = `import { Button } from 'antd';`;
|
||||
const sourceCode = 'import { Button } from \'antd\';';
|
||||
const result = await importStyle(sourceCode, { libraryName: 'antd', style: true });
|
||||
expect(result?.code).toBe(`${sourceCode}\nimport 'antd/es/button/style';`);
|
||||
});
|
||||
it('custom style', async () => {
|
||||
const sourceCode = `import { Button } from 'antd';`;
|
||||
const sourceCode = 'import { Button } from \'antd\';';
|
||||
const result = await importStyle(sourceCode, { libraryName: 'antd', style: (name) => `antd/es/${name.toLocaleLowerCase()}/style` });
|
||||
expect(result?.code).toBe(`${sourceCode}\nimport 'antd/es/button/style';`);
|
||||
});
|
||||
it('mismatch import', async () => {
|
||||
const sourceCode = `import { Button } from 'antd-mobile';`;
|
||||
const sourceCode = 'import { Button } from \'antd-mobile\';';
|
||||
const result = await importStyle(sourceCode, { libraryName: 'antd', style: true });
|
||||
expect(result?.code).toBe(`${sourceCode}`);
|
||||
});
|
||||
it('multiple import', async () => {
|
||||
const sourceCode = `import { Button, Table } from 'antd';`;
|
||||
const sourceCode = 'import { Button, Table } from \'antd\';';
|
||||
const result = await importStyle(sourceCode, { libraryName: 'antd', style: true });
|
||||
expect(result?.code).toBe(`${sourceCode}\nimport 'antd/es/button/style';\nimport 'antd/es/table/style';`);
|
||||
});
|
||||
it('named import', async () => {
|
||||
const sourceCode = `import { Button as Btn } from 'antd';`;
|
||||
const sourceCode = 'import { Button as Btn } from \'antd\';';
|
||||
const result = await importStyle(sourceCode, { libraryName: 'antd', style: true });
|
||||
expect(result?.code).toBe(`${sourceCode}\nimport 'antd/es/button/style';`);
|
||||
});
|
||||
it('default import', async () => {
|
||||
const sourceCode = `import * as antd from 'antd';`;
|
||||
const sourceCode = 'import * as antd from \'antd\';';
|
||||
const result = await importStyle(sourceCode, { libraryName: 'antd', style: true });
|
||||
expect(result?.code).toBe(`${sourceCode}`);
|
||||
});
|
||||
it('sourcemap', async () => {
|
||||
const sourceCode = `import * as antd from 'antd';`;
|
||||
const sourceCode = 'import * as antd from \'antd\';';
|
||||
const result = await importStyle(sourceCode, { libraryName: 'antd', style: true, sourceMap: true });
|
||||
expect(!!result?.map).toBe(true);
|
||||
});
|
||||
it('none import', async () => {
|
||||
const sourceCode = `export const a = 'antd';`;
|
||||
const sourceCode = 'export const a = \'antd\';';
|
||||
const result = await importStyle(sourceCode, { libraryName: 'antd', style: true });
|
||||
expect(result).toBe(null);
|
||||
});
|
||||
|
||||
|
||||
it('parse error', async () => {
|
||||
const sourceCode = `export antd, { Button } from 'antd';`;
|
||||
const sourceCode = 'export antd, { Button } from \'antd\';';
|
||||
const result = await importStyle(sourceCode, { libraryName: 'antd', style: true });
|
||||
expect(result).toBe(null);
|
||||
});
|
||||
it('import error', async () => {
|
||||
const sourceCode = `import antd, { Button } from 'antd';`;
|
||||
const sourceCode = 'import antd, { Button } from \'antd\';';
|
||||
const result = await importStyle(sourceCode, { libraryName: 'antd', style: true });
|
||||
expect(result?.code).toBe(sourceCode);
|
||||
});
|
||||
it('style false', async () => {
|
||||
const sourceCode = `import { Button } from 'antd';`;
|
||||
const sourceCode = 'import { Button } from \'antd\';';
|
||||
const result = await importStyle(sourceCode, { libraryName: 'antd', style: false });
|
||||
expect(result).toBe(null);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -144,8 +144,12 @@ export interface RouteModules {
|
|||
export interface AssetsManifest {
|
||||
dataLoader?: string;
|
||||
publicPath: string;
|
||||
entries: string[];
|
||||
pages: string[];
|
||||
entries: {
|
||||
[assetPath: string]: string[];
|
||||
};
|
||||
pages: {
|
||||
[assetPath: string]: string[];
|
||||
};
|
||||
assets?: {
|
||||
[assetPath: string]: string;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { expect, describe, it } from 'vitest';
|
||||
import * as path from 'path';
|
||||
import * as fs from 'fs';
|
||||
import { fileURLToPath } from 'url';
|
||||
import { expect, describe, it } from 'vitest';
|
||||
import { redirectImport } from '../src/unPlugins/redirectImport';
|
||||
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
|
|
@ -14,8 +14,8 @@ describe('redirect import', () => {
|
|||
specifier: 'Head',
|
||||
source: 'react-helmet',
|
||||
exportAlias: {
|
||||
'Head': 'Helmet',
|
||||
}
|
||||
Head: 'Helmet',
|
||||
},
|
||||
}, {
|
||||
specifier: 'store',
|
||||
source: '@ice/store',
|
||||
|
|
@ -53,5 +53,5 @@ describe('redirect import', () => {
|
|||
const code = fs.readFileSync(path.join(__dirname, './fixtures/multiple.js'), 'utf-8');
|
||||
const transformed = await redirectImport(code, { exportData, targetSource: 'ice' });
|
||||
expect(transformed).toBe('import request from \'axios\';\nimport store from \'@ice/store\';');
|
||||
})
|
||||
})
|
||||
});
|
||||
});
|
||||
|
|
@ -76,6 +76,35 @@ importers:
|
|||
typescript: 4.7.4
|
||||
vitest: 0.15.2_c8@7.12.0+jsdom@20.0.0
|
||||
|
||||
examples/app-config:
|
||||
specifiers:
|
||||
'@ice/app': workspace:*
|
||||
'@ice/plugin-auth': workspace:*
|
||||
'@ice/plugin-rax-compat': workspace:*
|
||||
'@ice/runtime': workspace:*
|
||||
'@types/react': ^18.0.0
|
||||
'@types/react-dom': ^18.0.2
|
||||
'@uni/env': ^1.1.0
|
||||
ahooks: ^3.3.8
|
||||
react: ^18.0.0
|
||||
react-dom: ^18.0.0
|
||||
speed-measure-webpack-plugin: ^1.5.0
|
||||
webpack: ^5.73.0
|
||||
dependencies:
|
||||
'@ice/app': link:../../packages/ice
|
||||
'@ice/plugin-auth': link:../../packages/plugin-auth
|
||||
'@ice/plugin-rax-compat': link:../../packages/plugin-rax-compat
|
||||
'@ice/runtime': link:../../packages/runtime
|
||||
'@uni/env': 1.1.0
|
||||
ahooks: 3.4.1_react@18.2.0
|
||||
react: 18.2.0
|
||||
react-dom: 18.2.0_react@18.2.0
|
||||
devDependencies:
|
||||
'@types/react': 18.0.17
|
||||
'@types/react-dom': 18.0.6
|
||||
speed-measure-webpack-plugin: 1.5.0_webpack@5.74.0
|
||||
webpack: 5.74.0
|
||||
|
||||
examples/basic-project:
|
||||
specifiers:
|
||||
'@ice/app': workspace:*
|
||||
|
|
@ -539,7 +568,7 @@ importers:
|
|||
semver: ^7.3.5
|
||||
temp: ^0.9.4
|
||||
trusted-cert: ^1.1.3
|
||||
unplugin: ^0.8.0
|
||||
unplugin: ^0.9.0
|
||||
webpack: ^5.73.0
|
||||
webpack-dev-server: ^4.7.4
|
||||
dependencies:
|
||||
|
|
@ -596,7 +625,7 @@ importers:
|
|||
'@types/temp': 0.9.1
|
||||
chokidar: 3.5.3
|
||||
react: 18.2.0
|
||||
unplugin: 0.8.1_3qmdnfoccgmcaqi5n264fyeyfi
|
||||
unplugin: 0.9.5_3qmdnfoccgmcaqi5n264fyeyfi
|
||||
webpack: 5.74.0_esbuild@0.14.54
|
||||
webpack-dev-server: 4.8.1_webpack@5.74.0
|
||||
|
||||
|
|
@ -18258,31 +18287,6 @@ packages:
|
|||
resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==}
|
||||
engines: {node: '>= 0.8'}
|
||||
|
||||
/unplugin/0.8.1_3qmdnfoccgmcaqi5n264fyeyfi:
|
||||
resolution: {integrity: sha512-o7rUZoPLG1fH4LKinWgb77gDtTE6mw/iry0Pq0Z5UPvZ9+HZ1/4+7fic7t58s8/CGkPrDpGq+RltO+DmswcR4g==}
|
||||
peerDependencies:
|
||||
esbuild: '>=0.13'
|
||||
rollup: ^2.50.0
|
||||
vite: ^2.3.0 || ^3.0.0-0
|
||||
webpack: 4 || 5
|
||||
peerDependenciesMeta:
|
||||
esbuild:
|
||||
optional: true
|
||||
rollup:
|
||||
optional: true
|
||||
vite:
|
||||
optional: true
|
||||
webpack:
|
||||
optional: true
|
||||
dependencies:
|
||||
acorn: 8.8.0
|
||||
chokidar: 3.5.3
|
||||
esbuild: 0.14.54
|
||||
webpack: 5.74.0_esbuild@0.14.54
|
||||
webpack-sources: 3.2.3
|
||||
webpack-virtual-modules: 0.4.4
|
||||
dev: true
|
||||
|
||||
/unplugin/0.9.5_3qmdnfoccgmcaqi5n264fyeyfi:
|
||||
resolution: {integrity: sha512-luraheyfxwtvkvHpsOvMNv7IjLdORTWKZp0gWYNHGLi2ImON3iIZOj464qEyyEwLA/EMt12fC415HW9zRpOfTg==}
|
||||
peerDependencies:
|
||||
|
|
|
|||
|
|
@ -0,0 +1,38 @@
|
|||
import { expect, test, describe, afterAll } from 'vitest';
|
||||
import { buildFixture, setupBrowser } from '../utils/build';
|
||||
import { startFixture, setupStartBrowser } from '../utils/start';
|
||||
import type { Page } from '../utils/browser';
|
||||
import type Browser from '../utils/browser';
|
||||
|
||||
const example = 'app-config';
|
||||
|
||||
describe(`build ${example}`, () => {
|
||||
test('open /', async () => {
|
||||
await buildFixture(example);
|
||||
await setupBrowser({ example });
|
||||
}, 120000);
|
||||
});
|
||||
|
||||
describe(`start ${example}`, () => {
|
||||
let page: Page;
|
||||
let browser: Browser;
|
||||
|
||||
test('setup devServer', async () => {
|
||||
const { devServer, port } = await startFixture(example);
|
||||
const res = await setupStartBrowser({ server: devServer, port });
|
||||
page = res.page;
|
||||
browser = res.browser;
|
||||
await page.push('ice');
|
||||
expect(await page.$$text('h1')).toStrictEqual(['home']);
|
||||
}, 120000);
|
||||
|
||||
test('error page', async () => {
|
||||
await page.push('ice/error');
|
||||
await page.waitForNetworkIdle();
|
||||
expect(await page.$$text('h1')).toStrictEqual(['Something went wrong.']);
|
||||
}, 120000);
|
||||
|
||||
afterAll(async () => {
|
||||
await browser.close();
|
||||
});
|
||||
});
|
||||
|
|
@ -1,10 +1,11 @@
|
|||
import { expect, test, describe, afterAll } from 'vitest';
|
||||
import * as path from 'path';
|
||||
import * as fs from 'fs';
|
||||
import { fileURLToPath } from 'url';
|
||||
import { expect, test, describe, afterAll } from 'vitest';
|
||||
import { buildFixture, setupBrowser } from '../utils/build';
|
||||
import { startFixture, setupStartBrowser } from '../utils/start';
|
||||
import Browser, { Page } from '../utils/browser';
|
||||
import type { Page } from '../utils/browser';
|
||||
import type Browser from '../utils/browser';
|
||||
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
|
||||
|
|
@ -44,7 +45,7 @@ describe(`build ${example}`, () => {
|
|||
|
||||
test('disable splitChunks', async () => {
|
||||
await buildFixture(example, {
|
||||
config: 'splitChunks.config.mts'
|
||||
config: 'splitChunks.config.mts',
|
||||
});
|
||||
const res = await setupBrowser({ example });
|
||||
page = res.page;
|
||||
|
|
@ -62,47 +63,73 @@ describe(`build ${example}`, () => {
|
|||
describe(`start ${example}`, () => {
|
||||
let page: Page;
|
||||
let browser: Browser;
|
||||
const rootDir = path.join(__dirname, `../../examples/${example}`);
|
||||
|
||||
test('setup devServer', async () => {
|
||||
const { devServer, port } = await startFixture(example);
|
||||
const { devServer, port } = await startFixture(example, {
|
||||
mock: true,
|
||||
force: true,
|
||||
https: false,
|
||||
analyzer: false,
|
||||
open: false,
|
||||
mode: 'start',
|
||||
});
|
||||
const res = await setupStartBrowser({ server: devServer, port });
|
||||
page = res.page;
|
||||
browser = res.browser;
|
||||
expect(await page.$$text('h2')).toStrictEqual(['Home Page']);
|
||||
expect(await page.$$text('#data-from')).toStrictEqual(['getServerData']);
|
||||
}, 120000);
|
||||
// TODO: fix waitForNetworkIdle not resolved
|
||||
test.skip('should update config during client routing', async () => {
|
||||
const { devServer, port } = await startFixture(example);
|
||||
const res = await setupStartBrowser({ server: devServer, port });
|
||||
page = res.page;
|
||||
browser = res.browser;
|
||||
|
||||
test('update route', async () => {
|
||||
const targetPath = path.join(rootDir, 'src/pages/blog.tsx');
|
||||
const routeContent = fs.readFileSync(targetPath, 'utf-8');
|
||||
const routeManifest = fs.readFileSync(path.join(rootDir, '.ice/route-manifest.json'), 'utf-8');
|
||||
fs.writeFileSync(targetPath, routeContent);
|
||||
await page.reload();
|
||||
expect(JSON.parse(routeManifest)[0].children.length).toBe(3);
|
||||
}, 120000);
|
||||
|
||||
test('update watched file: global.css', async () => {
|
||||
const targetPath = path.join(rootDir, 'src/global.css');
|
||||
const cssContent = fs.readFileSync(targetPath, 'utf-8');
|
||||
fs.writeFileSync(targetPath, cssContent);
|
||||
await page.reload();
|
||||
});
|
||||
|
||||
test('update watched file: app.ts', async () => {
|
||||
const targetPath = path.join(rootDir, 'src/app.tsx');
|
||||
const appContent = fs.readFileSync(targetPath, 'utf-8');
|
||||
fs.writeFileSync(targetPath, appContent);
|
||||
await page.reload();
|
||||
});
|
||||
|
||||
test('should update config during client routing', async () => {
|
||||
expect(
|
||||
await page.title()
|
||||
await page.title(),
|
||||
).toBe('Home');
|
||||
|
||||
expect(
|
||||
await page.$$attr('meta[name="theme-color"]', 'content')
|
||||
await page.$$attr('meta[name="theme-color"]', 'content'),
|
||||
).toStrictEqual(['#000']);
|
||||
|
||||
await page.click('a[href="/about"]');
|
||||
await page.push('about');
|
||||
await page.waitForNetworkIdle();
|
||||
|
||||
expect(
|
||||
await page.title()
|
||||
await page.title(),
|
||||
).toBe('About');
|
||||
|
||||
expect(
|
||||
await page.$$attr('meta[name="theme-color"]', 'content')
|
||||
await page.$$attr('meta[name="theme-color"]', 'content'),
|
||||
).toStrictEqual(['#eee']);
|
||||
|
||||
expect(
|
||||
await page.$$eval('link[href*="bootstrap"]', (els) => els.length)
|
||||
await page.$$eval('link[href*="bootstrap"]', (els) => els.length),
|
||||
).toBe(1);
|
||||
|
||||
expect(
|
||||
await page.$$eval('script[src*="lodash"]', (els) => els.length)
|
||||
await page.$$eval('script[src*="lodash"]', (els) => els.length),
|
||||
).toBe(1);
|
||||
}, 120000);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { expect, test, describe, afterAll } from 'vitest';
|
||||
import { buildFixture, setupBrowser } from '../utils/build';
|
||||
import { startFixture, setupStartBrowser } from '../utils/start';
|
||||
import Browser from '../utils/browser';
|
||||
import type Browser from '../utils/browser';
|
||||
import type { Page } from '../utils/browser';
|
||||
|
||||
const example = 'hash-router';
|
||||
|
|
@ -15,8 +15,8 @@ describe(`build ${example}`, () => {
|
|||
const res = await setupBrowser({ example, disableJS: false });
|
||||
page = res.page as Page;
|
||||
browser = res.browser;
|
||||
await page.waitForFunction(`document.getElementsByTagName('h1').length > 0`);
|
||||
await page.waitForFunction(`document.getElementsByTagName('h2').length > 0`);
|
||||
await page.waitForFunction('document.getElementsByTagName(\'h1\').length > 0');
|
||||
await page.waitForFunction('document.getElementsByTagName(\'h2\').length > 0');
|
||||
expect(await page.$$text('h1')).toStrictEqual(['Layout']);
|
||||
expect(await page.$$text('h2')).toStrictEqual(['Home']);
|
||||
}, 120000);
|
||||
|
|
@ -28,15 +28,13 @@ describe(`build ${example}`, () => {
|
|||
|
||||
describe(`start ${example}`, () => {
|
||||
let page: Page;
|
||||
let browser: Browser;
|
||||
|
||||
test('open /', async () => {
|
||||
const { devServer, port } = await startFixture(example);
|
||||
const res = await setupStartBrowser({ server: devServer, port });
|
||||
page = res.page;
|
||||
browser = res.browser;
|
||||
await page.waitForFunction(`document.getElementsByTagName('h1').length > 0`);
|
||||
await page.waitForFunction(`document.getElementsByTagName('h2').length > 0`);
|
||||
await page.waitForFunction('document.getElementsByTagName(\'h1\').length > 0');
|
||||
await page.waitForFunction('document.getElementsByTagName(\'h2\').length > 0');
|
||||
expect(await page.$$text('h1')).toStrictEqual(['Layout']);
|
||||
expect(await page.$$text('h2')).toStrictEqual(['Home']);
|
||||
}, 120000);
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import * as fs from 'fs';
|
|||
import { expect, test, describe, afterAll } from 'vitest';
|
||||
import { buildFixture, setupBrowser } from '../utils/build';
|
||||
import { startFixture, setupStartBrowser } from '../utils/start';
|
||||
import { Page } from '../utils/browser';
|
||||
import type { Page } from '../utils/browser';
|
||||
|
||||
const example = 'rax-project';
|
||||
|
||||
|
|
@ -39,7 +39,7 @@ describe(`start ${example}`, () => {
|
|||
const res = await setupStartBrowser({ server: devServer, port });
|
||||
page = res.page;
|
||||
browser = res.browser;
|
||||
await page.waitForFunction(`document.getElementsByTagName('span').length > 0`);
|
||||
await page.waitForFunction('document.getElementsByTagName(\'span\').length > 0');
|
||||
expect((await page.$$text('span')).length).toEqual(3);
|
||||
expect((await page.$$text('span'))[0]).toStrictEqual('Welcome to Your Rax App');
|
||||
expect((await page.$$text('span'))[1]).toStrictEqual('More information about Rax');
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { expect, test, describe, afterAll } from 'vitest';
|
||||
import { buildFixture, setupBrowser } from '../utils/build';
|
||||
import { startFixture, setupStartBrowser } from '../utils/start';
|
||||
import { Page } from '../utils/browser';
|
||||
import type { Page } from '../utils/browser';
|
||||
|
||||
const example = 'routes-generate';
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { expect, test, describe, afterAll } from 'vitest';
|
||||
import * as path from 'path';
|
||||
import * as fs from 'fs';
|
||||
import { expect, test, describe, afterAll } from 'vitest';
|
||||
import { buildFixture } from '../utils/build';
|
||||
|
||||
const example = 'single-route';
|
||||
|
|
@ -11,22 +11,20 @@ describe(`build ${example}`, () => {
|
|||
|
||||
test('optimize router', async () => {
|
||||
await buildFixture(example, {
|
||||
config: 'optimization.config.mts'
|
||||
config: 'optimization.config.mts',
|
||||
});
|
||||
const dataLoaderPath = path.join(__dirname, `../../examples/${example}/build/js/framework.js`);
|
||||
sizeWithOptimize = fs.statSync(dataLoaderPath).size;
|
||||
|
||||
}, 120000);
|
||||
|
||||
test('disable optimize router', async () => {
|
||||
await buildFixture(example);
|
||||
const dataLoaderPath = path.join(__dirname, `../../examples/${example}/build/js/framework.js`);
|
||||
sizeWithoutOptimize = fs.statSync(dataLoaderPath).size;
|
||||
|
||||
}, 120000);
|
||||
|
||||
afterAll(async () => {
|
||||
expect(sizeWithOptimize).toBeLessThan(sizeWithoutOptimize);
|
||||
expect(sizeWithoutOptimize - sizeWithOptimize).toBeGreaterThan(6 * 1024) // reduce more than 6kb after minify
|
||||
expect(sizeWithoutOptimize - sizeWithOptimize).toBeGreaterThan(6 * 1024); // reduce more than 6kb after minify
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ describe(`build ${example}`, () => {
|
|||
const res = await setupBrowser({ example, disableJS: false });
|
||||
page = res.page;
|
||||
browser = res.browser;
|
||||
await page.waitForFunction(`document.getElementsByTagName('button').length > 0`);
|
||||
await page.waitForFunction('document.getElementsByTagName(\'button\').length > 0');
|
||||
expect(await page.$$text('#username')).toStrictEqual(['name: ICE 3']);
|
||||
expect(await page.$$text('#count')).toStrictEqual(['0']);
|
||||
}, 120000);
|
||||
|
|
|
|||
|
|
@ -1,15 +1,15 @@
|
|||
import http from 'http';
|
||||
import url from 'url';
|
||||
import fse from 'fs-extra';
|
||||
import path from 'path';
|
||||
import fse from 'fs-extra';
|
||||
import puppeteer from 'puppeteer';
|
||||
|
||||
export interface IPage extends puppeteer.Page {
|
||||
html?: () => Promise<string>;
|
||||
$text?: (selector: string, trim?: boolean) => Promise<string|null>;
|
||||
$$text?: (selector: string, trim?: boolean) => Promise<(string|null)[]>;
|
||||
$attr?: (selector: string, attr: string) => Promise<string|null>;
|
||||
$$attr?: (selector: string, attr: string) => Promise<(string|null)[]>;
|
||||
$text?: (selector: string, trim?: boolean) => Promise<string | null>;
|
||||
$$text?: (selector: string, trim?: boolean) => Promise<(string | null)[]>;
|
||||
$attr?: (selector: string, attr: string) => Promise<string | null>;
|
||||
$$attr?: (selector: string, attr: string) => Promise<(string | null)[]>;
|
||||
push?: (url: string, options?: puppeteer.WaitForOptions & { referer?: string }) => Promise<puppeteer.HTTPResponse>;
|
||||
}
|
||||
|
||||
|
|
@ -24,7 +24,7 @@ export default class Browser {
|
|||
private browser: puppeteer.Browser;
|
||||
private baseUrl: string;
|
||||
|
||||
constructor (options: IBrowserOptions) {
|
||||
constructor(options: BrowserOptions) {
|
||||
const { server } = options;
|
||||
if (server) {
|
||||
this.server = server;
|
||||
|
|
@ -74,17 +74,17 @@ export default class Browser {
|
|||
}).listen(port, '127.0.0.1');
|
||||
}
|
||||
|
||||
async start () {
|
||||
async start() {
|
||||
this.browser = await puppeteer.launch();
|
||||
}
|
||||
|
||||
async close () {
|
||||
if (!this.browser) { return }
|
||||
async close() {
|
||||
if (!this.browser) { return; }
|
||||
await this.browser.close();
|
||||
this.server.close();
|
||||
}
|
||||
|
||||
async page (url: string, disableJS?: boolean) {
|
||||
async page(url: string, disableJS?: boolean) {
|
||||
this.baseUrl = url;
|
||||
if (!this.browser) { throw new Error('Please call start() before page(url)'); }
|
||||
const page: Page = await this.browser.newPage();
|
||||
|
|
@ -98,11 +98,11 @@ export default class Browser {
|
|||
page.html = () =>
|
||||
page.evaluate(() => window.document.documentElement.outerHTML);
|
||||
page.$text = (selector, trim) => page.$eval(selector, (el, trim) => {
|
||||
return trim ? (el.textContent || '').replace(/^\s+|\s+$/g, '') : el.textContent
|
||||
return trim ? (el.textContent || '').replace(/^\s+|\s+$/g, '') : el.textContent;
|
||||
}, trim);
|
||||
page.$$text = (selector, trim) =>
|
||||
page.$$eval(selector, (els, trim) => els.map((el) => {
|
||||
return trim ? (el.textContent || '').replace(/^\s+|\s+$/g, '') : el.textContent
|
||||
return trim ? (el.textContent || '').replace(/^\s+|\s+$/g, '') : el.textContent;
|
||||
}), trim);
|
||||
|
||||
page.$attr = (selector, attr) =>
|
||||
|
|
|
|||
|
|
@ -1,9 +1,10 @@
|
|||
import path from 'path';
|
||||
import process from 'process';
|
||||
import getPort from 'get-port';
|
||||
import Browser, { Page } from './browser';
|
||||
import createService from '../../packages/ice/src/createService';
|
||||
import { fileURLToPath } from 'url';
|
||||
import getPort from 'get-port';
|
||||
import createService from '../../packages/ice/src/createService';
|
||||
import type { Page } from './browser';
|
||||
import Browser from './browser';
|
||||
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
|
||||
|
|
@ -22,14 +23,16 @@ interface IReturn {
|
|||
}
|
||||
|
||||
// get builtIn plugins
|
||||
export const buildFixture = async function(example: string, commandArgs?: Record<string, string>) {
|
||||
export const buildFixture = async function (example: string, commandArgs?: Record<string, string>) {
|
||||
const rootDir = path.join(__dirname, `../../examples/${example}`);
|
||||
process.env.DISABLE_FS_CACHE = 'true';
|
||||
const service = await createService({ rootDir, command: 'build', commandArgs: {
|
||||
const service = await createService({ rootDir,
|
||||
command: 'build',
|
||||
commandArgs: {
|
||||
...(commandArgs || {}),
|
||||
} });
|
||||
await service.run();
|
||||
}
|
||||
};
|
||||
|
||||
export const setupBrowser: SetupBrowser = async (options) => {
|
||||
const { example, outputDir = 'build', defaultHtml = 'index.html', disableJS = true } = options;
|
||||
|
|
@ -37,11 +40,12 @@ export const setupBrowser: SetupBrowser = async (options) => {
|
|||
const port = await getPort();
|
||||
const browser = new Browser({ cwd: path.join(rootDir, outputDir), port });
|
||||
await browser.start();
|
||||
console.log()
|
||||
// when preview html generate by build, the path will not match the router info, so hydrate will not found the route component
|
||||
console.log();
|
||||
// When preview html generate by build, the path will not match the router info,
|
||||
// so hydrate will not found the route component.
|
||||
const page = await browser.page(`http://127.0.0.1:${port}/${defaultHtml}`, disableJS);
|
||||
return {
|
||||
browser,
|
||||
page,
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
|
|
|||
|
|
@ -6,4 +6,4 @@ export default (order: string, cwd: string) => {
|
|||
stdio: 'inherit',
|
||||
cwd,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,14 +1,15 @@
|
|||
import path from 'path';
|
||||
import getPort from 'get-port';
|
||||
import Browser, { Page } from './browser';
|
||||
import { Server } from 'http';
|
||||
import createService from '../../packages/ice/src/createService';
|
||||
import type { Server } from 'http';
|
||||
import { fileURLToPath } from 'url';
|
||||
import getPort from 'get-port';
|
||||
import createService from '../../packages/ice/src/createService';
|
||||
import type { Page } from './browser';
|
||||
import Browser from './browser';
|
||||
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
|
||||
interface ISetupBrowser {
|
||||
(options: { port: number; defaultPath?: string; server: Server; }): Promise<IReturn>;
|
||||
interface SetupBrowser {
|
||||
(options: { port: number; defaultPath?: string; server: Server }): Promise<ReturnValue>;
|
||||
}
|
||||
|
||||
interface IReturn {
|
||||
|
|
@ -17,25 +18,28 @@ interface IReturn {
|
|||
}
|
||||
|
||||
// get builtIn plugins
|
||||
export const startFixture = async function (example: string) {
|
||||
export const startFixture = async function (example: string, commandArgs?: Record<string, any>) {
|
||||
const port = await getPort();
|
||||
const rootDir = path.join(__dirname, `../../examples/${example}`);
|
||||
const processCwdSpy = jest.spyOn(process, 'cwd');
|
||||
processCwdSpy.mockReturnValue(rootDir);
|
||||
process.env.DISABLE_FS_CACHE = 'true';
|
||||
const service = await createService({ rootDir, command: 'start', commandArgs: {
|
||||
const service = await createService({ rootDir,
|
||||
command: 'start',
|
||||
commandArgs: {
|
||||
host: '0.0.0.0',
|
||||
port,
|
||||
open: false,
|
||||
}});
|
||||
...commandArgs,
|
||||
} });
|
||||
|
||||
// @ts-ignore
|
||||
const { compiler, devServer } = await service.run();
|
||||
// wait generate assets manifest
|
||||
await new Promise((resolve) => {
|
||||
compiler.hooks.done.tap('done',() => {
|
||||
compiler.hooks.done.tap('done', () => {
|
||||
resolve(true);
|
||||
})
|
||||
});
|
||||
});
|
||||
|
||||
// @ts-ignore
|
||||
|
|
@ -60,7 +64,7 @@ export const startFixture = async function (example: string) {
|
|||
}) as any as Server;
|
||||
return {
|
||||
port,
|
||||
devServer
|
||||
devServer,
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -3,4 +3,4 @@ export default ({ modifyUserConfig }) => {
|
|||
modifyUserConfig('minify', false);
|
||||
// disable sourceMap to speed-up fixture start
|
||||
modifyUserConfig('sourceMap', false);
|
||||
}
|
||||
};
|
||||
|
|
@ -24,6 +24,8 @@ export default defineConfig({
|
|||
include: ['**/packages/**'],
|
||||
exclude: [
|
||||
'**/bundles/compiled/**',
|
||||
// App runtime has been tested by unit test case
|
||||
'**/packages/runtime/esm/**',
|
||||
'**/tests/**',
|
||||
],
|
||||
},
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ const path = require('path');
|
|||
module.exports = function (config, options) {
|
||||
return {
|
||||
name: 'docusaurus-redirect-plugin',
|
||||
async contentLoaded({ content, actions }) {
|
||||
async contentLoaded({ actions }) {
|
||||
const { createData, addRoute } = actions;
|
||||
|
||||
const routes = [
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import React, { useEffect } from 'react';
|
||||
import { useEffect } from 'react';
|
||||
|
||||
export default function Redirect(props) {
|
||||
const { location, history, redirectConfig = [] } = props;
|
||||
|
|
@ -28,7 +28,7 @@ export default function Redirect(props) {
|
|||
console.log('未知路由', pathname);
|
||||
}
|
||||
}
|
||||
}, []);
|
||||
}, [location, history, pathname, redirectConfig]);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import React, { useEffect, useState } from 'react';
|
||||
import React, { useState } from 'react';
|
||||
import clsx from 'clsx';
|
||||
import storage from '../utils/storage';
|
||||
import { isIntranet } from '../utils/internal';
|
||||
|
|
|
|||
Loading…
Reference in New Issue