chore: first commit

This commit is contained in:
鲲尘 2022-01-27 14:32:38 +08:00 committed by ClarkXia
parent ac910ed2dc
commit 26ffa95e90
69 changed files with 7621 additions and 0 deletions

3
.commitlintrc.js Normal file
View File

@ -0,0 +1,3 @@
const { getCommitlintConfig } = require('@iceworks/spec');
module.exports = getCommitlintConfig('common');

23
.eslintignore Normal file
View File

@ -0,0 +1,23 @@
# 忽略目录
build/
test/
tests/
node_modules/
dist/
out/
# node 覆盖率文件
coverage/
# 忽略测试文件
/packages/*/__tests__
/packages/*/lib/
# 忽略第三方包
/vendor/loader.js
# 忽略文件
**/*-min.js
**/*.min.js
workspace/

54
.eslintrc.js Normal file
View File

@ -0,0 +1,54 @@
const { getESLintConfig } = require('@iceworks/spec');
const commonRules = {
'react/jsx-filename-extension': 0,
'no-underscore-dangle': 0,
'class-methods-use-this': 0,
'no-param-reassign': 0,
'no-console': 0,
'comma-dangle': 0,
'prefer-object-spread': 0,
'import/named': 0,
indent: 0,
semi: 2,
'react/react-in-jsx-scope': 0,
'jsx-a11y/html-has-lang': 0,
'react/static-property-placement': 0,
'no-multiple-empty-lines': 1,
'react/jsx-no-bind': 0
};
const jsRules = getESLintConfig('react', {
rules: commonRules
});
const tsRules = getESLintConfig('react-ts', {
rules: {
...commonRules,
'@typescript-eslint/ban-types': 0,
'@typescript-eslint/ban-ts-comment': 0,
'@typescript-eslint/no-explicit-any': 0,
'@typescript-eslint/interface-name-prefix': 0,
'@typescript-eslint/explicit-function-return-type': 0,
'@typescript-eslint/no-var-requires': 0,
'@typescript-eslint/explicit-module-boundary-types': 0,
'no-use-before-define': 0,
'no-unused-vars': 0,
'@typescript-eslint/no-unused-vars': 1,
'@typescript-eslint/ban-ts-ignore': 0,
}
});
delete tsRules.root;
module.exports = {
...jsRules,
overrides: [
{
...tsRules,
files: ['**/*.ts', '**/*.tsx'],
},
],
env: {
jest: true,
},
};

93
.github/CONTRIBUTING.md vendored Normal file
View File

@ -0,0 +1,93 @@
# Contributing Guide
Hi! I’m really excited that you are interested in contributing to ICE. Before submitting your contribution though, please make sure to take a moment and read through the following guidelines.
- [Setup Environment](#setup-environment)
- [Run Examples](#run-examples)
- [Publish Packages](publish-packages)
- [Pull Request Guidelines](#pull-request-guidelines)
- [Issue Reporting Guidelines](#issue-reporting-guidelines)
- [Git Commit Specific](#git-commit-specific)
## Setup Environment
clone repo and initialize the setup environment:
```bash
# 1. clone and setup
$ git clone git@github.com:ice-lab/icejs.git
$ cd icejs && npm run setup
# 2. watch packages
$ npm run watch
```
## Run Examples
We provide a lot of examples, you can run the examples:
```bash
$ cd examples/basic-spa
$ npm link ../../packages/icejs
$ npm start
```
## Publish Packages
When you need to release, you can execute the command:
```bash
$ npm run publish
# 1. ✔️ ✔️ ✔️ Checking the working tree status...
# 2. 📦 📦 📦 Building packages...
# 3. ⚡ ⚡ ⚡ Update package version automatically...
# 4. 🚀 🚀 🚀 Start publishing...
# 5. 🔖 🔖 🔖 Commit & Create tag'...
# 6. 💡 💡 💡 Start syncing...
```
* When you need to release a latest version, the tag will be created automatically, running `npm publish` will tag your package with the `latest` dist-tag.
* To publish a package with the `beta` dist-tag, you can choose to release rc、beta、alpha versions, the tag will not be created.
## Rollback Packages
When a serious bug occurs in the production environment, you can backtrack the package version:
```bash
# rollback packages
$ npm run rollback <version>
# sync packages
$ npm run sync
```
## Pull Request Guidelines
- Only code that's ready for release should be committed to the master branch. All development should be done in dedicated branches.
- Checkout a **new** topic branch from master branch, and merge back against master branch.
- Make sure `npm test` passes.
- If adding new feature:
- Add accompanying test case.
- Provide convincing reason to add this feature. Ideally you should open a suggestion issue first and have it greenlighted before working on it.
- If fixing a bug:
- If you are resolving a special issue, add `(fix #xxxx[,#xxx])` (#xxxx is the issue id) in your PR title for a better release log, e.g. `update entities encoding/decoding (fix #3899)`.
- Provide detailed description of the bug in the PR. Live demo preferred.
- Add appropriate test coverage if applicable.
## Issue Reporting Guidelines
- The issue list of this repo is **exclusively** for bug reports and feature requests. Non-conforming issues will be closed immediately.
- For simple beginner questions, you can get quick answers from
- For more complicated questions, you can use Google or StackOverflow. Make sure to provide enough information when asking your questions - this makes it easier for others to help you!
- Try to search for your issue, it may have already been answered or even fixed in the development branch.
- It is **required** that you clearly describe the steps necessary to reproduce the issue you are running into. Issues with no clear repro steps will not be triaged. If an issue labeled "need repro" receives no further input from the issue author for more than 5 days, it will be closed.
- For bugs that involves build setups, you can create a reproduction repository with steps in the README.
- If your issue is resolved but still open, don’t hesitate to close it. In case you found a solution by yourself, it could be helpful to explain how you fixed it.
## Git Commit Specific
- Your commits message must follow our [git commit specific](./GIT_COMMIT_SPECIFIC.md).
- We will check your commit message, if it does not conform to the specification, the commit will be automatically refused, make sure you have read the specification above.
- You could use `git cz` with a CLI interface to replace `git commit` command, it will help you to build a proper commit-message, see [commitizen](https://github.com/commitizen/cz-cli).
- It's OK to have multiple small commits as you work on your branch - we will let GitHub automatically squash it before merging.

39
.github/GIT_COMMIT_SPECIFIC.md vendored Normal file
View File

@ -0,0 +1,39 @@
# GIT COMMIT MESSAGE CHEAT SHEET
**Proposed format of the commit message**
```
<type>: <subject>
<body>
```
All lines are wrapped at 100 characters !
**Allowed `<type>`**
- feat (A new feature)
- fix (A bug fix)
- docs (Documentation only changes)
- style (Changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc))
- perf (A code change that improves performance)
- refactor (A code change that neither fixes a bug nor adds a feature)
- test (Adding missing tests or correcting existing tests)
- build (Changes that affect the build system or external dependencies (example scopes: gulp, broccoli, npm))
- ci (Changes to our CI configuration files and scripts (example scopes: Travis, Circle, BrowserStack, SauceLabs))
- chore (Other changes that don't modify src or test files)
- revert (Reverts a previous commit)
- release (Relase version)
**Breaking changes**
All breaking changes have to be mentioned in message body, on separated line:
​ _Breaks removed $browser.setUrl() method (use $browser.url(newUrl))_
​ _Breaks ng: repeat option is no longer supported on selects (use ng:options)_
**Message body**
- uses the imperative, present tense: “change” not “changed” nor “changes”
- includes motivation for the change and contrasts with previous behavior

View File

@ -0,0 +1,27 @@
---
name: 'Bug report'
about: 'Report ice.js, components, documents and other issues'
title: ''
labels: ''
assignees: ''
---
<!--
Fusion 组件相关问题反馈请移步:https://fusion.design/feedback
-->
## What is the current behavior? 发生了什么?
<!--
If the current behavior is a bug, please provide the steps to reproduce and if possible a minimal demo of the problem.
-->
<!-- 清晰的描述下遇到的问题,建议附上错误截图 -->
## What is the expected behavior? 期望的结果是什么?
## Any additional comments? 相关环境信息?
- **ice.js Version**
- **build.json Configuration**
- **Node Version**:
- **Platform**:

23
.github/workflows/1.x-publisher.yml vendored Normal file
View File

@ -0,0 +1,23 @@
name: 1.x Publisher
on:
push:
branches:
- stable/1.x
jobs:
build-and-publish-stable:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v1
with:
node-version: 10
registry-url: https://registry.npmjs.org/
- run: npm run setup
- run: npm run publish:stable
env:
NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}}
ACCESS_KEY_ID: ${{ secrets.ACCESS_KEY_ID }}
ACCESS_KEY_SECRET: ${{ secrets.ACCESS_KEY_SECRET }}
REGISTRY: https://registry.npmjs.org

23
.github/workflows/auto-publisher.yml vendored Normal file
View File

@ -0,0 +1,23 @@
name: Auto Publisher
on:
push:
branches:
- master
jobs:
build-and-publish:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v1
with:
node-version: 12
registry-url: https://registry.npmjs.org/
- run: npm run setup
- run: npm run publish
env:
NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}}
ACCESS_KEY_ID: ${{ secrets.ACCESS_KEY_ID }}
ACCESS_KEY_SECRET: ${{ secrets.ACCESS_KEY_SECRET }}
REGISTRY: https://registry.npmjs.org

24
.github/workflows/beta-publisher.yml vendored Normal file
View File

@ -0,0 +1,24 @@
name: Beta Publisher
on:
push:
branches:
- beta
jobs:
build-and-publish-beta:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: nelonoel/branch-name@v1
- uses: actions/setup-node@v1
with:
node-version: 12
registry-url: https://registry.npmjs.org/
- run: npm run setup
- run: npm run publish:beta
env:
NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}}
ACCESS_KEY_ID: ${{ secrets.ACCESS_KEY_ID }}
ACCESS_KEY_SECRET: ${{ secrets.ACCESS_KEY_SECRET }}
REGISTRY: https://registry.npmjs.org

34
.github/workflows/ci.yml vendored Normal file
View File

@ -0,0 +1,34 @@
name: CI
env:
NODE_OPTIONS: --max-old-space-size=6144
on: [push]
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [12.x]
steps:
- uses: actions/checkout@v2
- name: Set branch name
run: echo >>$GITHUB_ENV BRANCH_NAME=${GITHUB_REF#refs/heads/}
- name: Echo branch name
run: echo ${BRANCH_NAME}
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node-version }}
registry-url: https://registry.npmjs.org/
- run: npm run setup
- run: npm run dependency:check
- run: npm run lint
- run: npm run test
- run: npm run version:check
- run: npm run coverage
env:
ACCESS_KEY_ID: ${{ secrets.ACCESS_KEY_ID }}
ACCESS_KEY_SECRET: ${{ secrets.ACCESS_KEY_SECRET }}
CI: true

24
.github/workflows/contributors.yml vendored Normal file
View File

@ -0,0 +1,24 @@
name: Add contributors
on:
# schedule:
# - cron: '5 * * * *'
push:
branches:
- master
jobs:
add-contributors:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: BobAnkh/add-contributors@master
with:
REPO_NAME: "alibaba/ice"
CONTRIBUTOR: "## Contributors"
COLUMN_PER_ROW: "10"
ACCESS_TOKEN: ${{secrets.PERSONAL_GITHUB_TOKEN}}
IMG_WIDTH: "60"
FONT_SIZE: "14"
PATH: "/README.md"
COMMIT_MESSAGE: "docs(README): update contributors"
AVATAR_SHAPE: "round"

42
.gitignore vendored Normal file
View File

@ -0,0 +1,42 @@
# https://github.com/github/gitignore/blob/master/Node.gitignore
# Dependencies
node_modules
jspm_packages
# Only keep yarn.lock in the root
package-lock.json
*/**/yarn.lock
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
# Coverage directory used by tools like istanbul
coverage
*.lcov
# Others
.npm
.eslintcache
.idea
.DS_Store
.happypack
.vscode
.tmp
*.swp
*.dia~
*.temp.json
# Packages
packages/*/lib/
# temp folder .ice
examples/*/.ice
build
dist
.history

4
.husky/commit-msg Executable file
View File

@ -0,0 +1,4 @@
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
npx --no -- commitlint -E HUSKY_GIT_PARAMS

4
.husky/pre-commit Executable file
View File

@ -0,0 +1,4 @@
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
npm run lint

9
.prettierignore Normal file
View File

@ -0,0 +1,9 @@
node_modules
.ice
dist
*.d.ts
*.js
fixtures
*.md
*.yaml
*.less

8
.prettierrc Normal file
View File

@ -0,0 +1,8 @@
{
"printWidth": 80,
"singleQuote": true,
"trailingComma": "all",
"proseWrap": "never",
"overrides": [{ "files": ".prettierrc", "options": { "parser": "json" } }],
"plugins": ["prettier-plugin-organize-imports", "prettier-plugin-packagejson"]
}

22
LICENSE Normal file
View File

@ -0,0 +1,22 @@
MIT LICENSE
Copyright (c) 2018-present Alibaba Inc.
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

7
README.md Normal file
View File

@ -0,0 +1,7 @@
# icejs
> v3 develop version
## LICENSE
[MIT](https://github.com/ice-lab/ice-next/blob/master/LICENSE)

View File

@ -0,0 +1 @@
/src

View File

@ -0,0 +1 @@
# basic vite example

View File

@ -0,0 +1,21 @@
{
"hash": false,
"minify": "esbuild",
"polyfill": false,
"vite": true,
"vendor": false,
"plugins": [
"build-plugin-jsx-plus"
],
"sassLoaderOptions": {
"prependData": ".test{color:red}"
},
"tsChecker": true,
"proxy": {
"/api": {
"target": "http://jsonplaceholder.typicode.com",
"changeOrigin": true,
"pathRewrite": { "^/api": "/todos" }
}
}
}

View File

@ -0,0 +1,11 @@
const status = 'SUCCESS';
// mock/index.js
export default {
'GET /api/repo': {
status,
data: {
group: 'ice.js',
url: 'https://github.com/ice-lab/ice.js'
}
},
};

View File

@ -0,0 +1,17 @@
{
"name": "basic-vite",
"version": "0.0.1",
"scripts": {
"start": "../../packages/icejs/bin/ice-cli.js start",
"build": "../../packages/icejs/bin/ice-cli.js build"
},
"dependencies": {
"react": "^17.0.0",
"react-dom": "^17.0.0"
},
"devDependencies": {
"@types/react": "^17.0.0",
"@types/react-dom": "^17.0.0"
},
"license": "MIT"
}

View File

@ -0,0 +1,11 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite App</title>
</head>
<body>
<div id="ice-container"></div>
</body>
</html>

View File

@ -0,0 +1,8 @@
export default ({ children }) => {
return (
<div>
Layout
{children}
</div>
);
};

View File

@ -0,0 +1,10 @@
import { runApp, IAppConfig } from 'ice';
const appConfig: IAppConfig = {
app: {
rootId: 'ice-container',
errorBoundary: true,
},
};
runApp(appConfig);

View File

@ -0,0 +1,11 @@
body {
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu',
'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
code {
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', monospace;
}

View File

@ -0,0 +1,5 @@
export default () => {
return (
<div>Page1</div>
);
};

View File

@ -0,0 +1,5 @@
export default () => {
return (
<div>Page2</div>
);
};

View File

@ -0,0 +1,8 @@
export default ({ children }) => {
return (
<div>
Dashboard
{children}
</div>
);
};

View File

@ -0,0 +1,75 @@
:root {
--primay: black;
--bg-primay: white;
--link: #2a0b0b;
}
.App {
text-align: center;
}
.App-logo {
height: 40vmin;
pointer-events: none;
}
p {
margin: 20px 0px;
}
.header {
font-size: 3rem;
}
.body {
margin: 20px 0 10px;
font-size: 0.9rem;
}
button {
outline: none;
border: none;
border-radius: 8px;
padding: 10px 35px;
background: #845ec2;
color: white;
}
@media (prefers-reduced-motion: no-preference) {
.App-logo {
animation: App-logo-spin infinite 20s linear;
}
}
.App-header {
background-color: #282c34;
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
font-size: calc(10px + 2vmin);
background: var(--bg-primay);
color: var(--primay);
}
.App-link {
color: #61dafb;
color: var(--link);
}
@keyframes App-logo-spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
button {
font-size: calc(10px + 2vmin);
}

View File

@ -0,0 +1,52 @@
import { Head } from 'ice';
import { useState } from 'react';
import store from './store';
import './index.css';
function App() {
const [count, setCount] = useState<number>(0);
const [titleState] = store.useModel('title');
return (
<div className="App">
<Head>
<meta charSet="utf-8" />
<title>Home title</title>
<meta name="keywords" content="About Keywords" />
<meta name="description" content="About Description" />
</Head>
<header className="App-header">
<p x-if={titleState.title} className="header">{titleState.title}</p>
<div className="body">
<button type="button" onClick={() => setCount((e: number) => e + 1)}>
🪂 Click me : {count}
</button>
<p> Don&apos;t forget to install <a href="https://appworks.site/">AppWorks</a> in Your Vscode.</p>
<p>
<a
className="App-link"
href="https://reactjs.org"
target="_blank"
rel="noopener noreferrer"
>
Learn React
</a>
{' | '}
<a
className="App-link"
href="https://vitejs.dev/guide/features.html"
target="_blank"
rel="noopener noreferrer"
>
Vite Docs
</a>
</p>
</div>
</header>
</div>
);
}
export default App;

View File

@ -0,0 +1,10 @@
export default {
state: {
title: '🚀 Vite + Icejs'
},
reducers: {
update(prevState, payload) {
return { title: payload };
},
}
};

View File

@ -0,0 +1,6 @@
import { createStore } from 'ice';
import title from './models/title';
const store = createStore({ title });
export default store;

View File

@ -0,0 +1,40 @@
import { IRouterConfig, lazy } from 'ice';
import DashboardLayout from '@/pages/Dashboard';
import Dashboard1 from '@/pages/Dashboard/Page1';
import Dashboard2 from '@/pages/Dashboard/Page2';
import Layout from '@/Layout';
const Home = lazy(() => import('@/pages/Home'));
const routes: IRouterConfig[] = [
{
path: '/',
component: Home,
exact: true
},
{
path: '/',
component: Layout,
children: [
{
path: '/dashboard',
component: DashboardLayout,
children: [
{
path: '/a',
component: Dashboard1,
exact: true
},
{
path: '/b',
component: Dashboard2,
exact: true
}
]
},
]
},
];
export default routes;

View File

@ -0,0 +1,3 @@
import { createStore } from 'ice';
export default createStore({});

4
examples/basic-vite/src/typings.d.ts vendored Normal file
View File

@ -0,0 +1,4 @@
declare module '*.module.scss' {
const classes: { [key: string]: string };
export default classes;
}

View File

@ -0,0 +1,38 @@
{
"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,
"types": ["node", "jest", "vite/client"],
"paths": {
"@/*": [
"./src/*"
],
"ice": [
".ice/index.ts"
]
}
}
}

40
jest.config.js Normal file
View File

@ -0,0 +1,40 @@
/* const { getHookFiles } = require('./packages/icejs/lib/require-hook');
const moduleNameMapper = getHookFiles().reduce((mapper, [id, value]) => {
mapper[`^${id}$`] = value;
return mapper;
}, {}); */
module.exports = {
coverageDirectory: './coverage/',
collectCoverage: true,
collectCoverageFrom: ['packages/*/lib/*.{js,jsx}'],
coveragePathIgnorePatterns: [
'<rootDir>/node_modules/'
],
// copy from jest config
testEnvironment: 'node',
transform: {
'^.+\\.jsx?$': 'babel-jest',
'^.+\\.tsx?$': 'ts-jest'
},
roots: [
'<rootDir>/packages',
'<rootDir>/test',
],
testPathIgnorePatterns: [
'/node_modules/',
'/lib/',
'create-cli-utils/'
],
testMatch: ['**/__tests__/**/*.[jt]s?(x)', '**/?(*.)+(spec|test).[jt]s?(x)'],
// For ts-jest use rootDir's tsconfig.json, while unable to resolve references.
// The following strategy maybe not the best, but it works.
// https://github.com/kulshekhar/ts-jest/issues/1648
globals: {
'ts-jest': {
tsconfig: 'tsconfig.base.json',
},
},
};

51
package.json Normal file
View File

@ -0,0 +1,51 @@
{
"private": true,
"workspaces": [
"packages/*"
],
"description": "A universal framework based on React",
"scripts": {
"setup": "rm -rf node_modules && pnpm install && pnpm run build",
"watch": "esmo ./scripts/watch.ts",
"build": "esmo ./scripts/build.ts",
"clean": "rimraf packages/*/lib",
"lint": "eslint --cache --ext .js,.jsx,.ts,.tsx ./",
"lint:fix": "npm run lint -- --fix",
"test": "jest --forceExit --ci",
"test:watch": "jest --watch"
},
"author": "ice-admin@alibaba-inc.com",
"license": "MIT",
"devDependencies": {
"@commitlint/cli": "^16.1.0",
"@iceworks/spec": "^1.5.0",
"@types/cross-spawn": "^6.0.2",
"@types/fs-extra": "^9.0.13",
"@types/glob": "^7.2.0",
"@types/jest": "^27.4.0",
"@types/node": "^17.0.12",
"@types/pify": "^5.0.1",
"@types/semver": "^7.3.9",
"chokidar": "^3.5.3",
"dependency-check": "^4.1.0",
"eslint": "^8.7.0",
"esno": "^0.14.0",
"execa": "^6.0.0",
"fs-extra": "^10.0.0",
"glob": "^7.2.0",
"husky": "^7.0.4",
"ice-npm-utils": "^3.0.1",
"jest": "^27.4.7",
"pify": "^5.0.0",
"prettier": "^2.5.1",
"prettier-plugin-organize-imports": "^2.3.4",
"prettier-plugin-packagejson": "^2.2.15",
"puppeteer": "^13.1.2",
"react": "^17.0.0",
"rimraf": "^3.0.2",
"stylelint": "^14.3.0",
"ts-jest": "^27.1.3",
"typescript": "^4.5.5"
},
"packageManager": "pnpm"
}

View File

@ -0,0 +1,11 @@
{
"name": "icejs",
"version": "3.0.0",
"description": "ice",
"main": "lib/index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "ice-admin",
"license": "MIT"
}

View File

@ -0,0 +1 @@
export default () => 'ice';

View File

@ -0,0 +1,7 @@
import ice from '../src/index';
describe('basic test', () => {
it('test return value', () => {
expect(ice()).toBe('ice');
});
});

View File

@ -0,0 +1,9 @@
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"rootDir": "src",
"outDir": "lib",
"esModuleInterop": true
},
"include": ["src"]
}

View File

@ -0,0 +1,15 @@
{
"name": "plugin-app",
"version": "1.0.0",
"description": "plugin app",
"main": "lib/index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "ice-admin",
"files": [
"runtime",
"lib"
],
"license": "MIT"
}

View File

@ -0,0 +1,3 @@
export default () => {
return 'plugin runtime';
};

View File

@ -0,0 +1 @@
export default () => 'plugin';

View File

@ -0,0 +1,7 @@
import ice from '../src/index';
describe('basic test', () => {
it('test return value', () => {
expect(ice()).toBe('plugin');
});
});

View File

@ -0,0 +1,9 @@
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"baseUrl": "./",
"rootDir": "src",
"outDir": "lib"
},
"include": ["src"]
}

5966
pnpm-lock.yaml Normal file

File diff suppressed because it is too large Load Diff

3
pnpm-workspace.yaml Normal file
View File

@ -0,0 +1,3 @@
packages:
- 'packages/*'
- 'examples/*'

27
scripts/build.ts Normal file
View File

@ -0,0 +1,27 @@
import glob from 'glob';
import * as path from 'path';
import * as fs from 'fs-extra';
import { run } from './shell';
(async () => {
await run('npm run clean');
const fileParten = '*/src/**/!(*.ts|*.tsx|*.rs)';
console.log(`[COPY]: ${fileParten}`);
const cwd = path.join(process.cwd(), 'packages');
const files = glob.sync(fileParten, { cwd, nodir: true });
// eslint-disable-next-line
for (const file of files) {
const from = path.join(cwd, file);
const to = path.join(cwd, file.replace(/\/src\//, '/lib/'));
// eslint-disable-next-line
await fs.mkdirp(path.dirname(to));
// eslint-disable-next-line
await fs.copyFile(from, to);
}
await run('npx tsc --build ./tsconfig.json');
})().catch((e) => {
console.trace(e);
process.exit(128);
});

View File

@ -0,0 +1,18 @@
import { execaCommand } from 'execa';
import getPackages from './getPackages';
// eslint-disable-next-line
const chalk = require('chalk');
(async () => {
const { packageDirs } = await getPackages();
packageDirs.forEach((pkgDir) => {
execaCommand(`dependency-check ${pkgDir} --missing`, {
cwd: pkgDir,
stdio: 'inherit'
});
});
})().catch((e) => {
console.log(chalk.red('\n ⚠️ ⚠️ ⚠️ 依赖检查失败\n\n'), e);
process.exit(128);
});

View File

@ -0,0 +1,84 @@
import { existsSync, readdirSync, readFileSync } from 'fs';
import { join } from 'path';
import { getNpmInfo } from 'ice-npm-utils';
import * as semver from 'semver';
const TARGET_DIRECTORY = join(__dirname, '../packages');
export interface IPackageInfo {
name: string;
directory: string;
localVersion: string;
publishVersion: string;
mainFile: string; // package.json main file
shouldPublish: boolean;
packageInfo: {
[key: string]: any;
};
}
function checkBuildSuccess(directory: string, mainFile: string): boolean {
const isExist = existsSync(join(directory, mainFile));
if (!isExist) {
throw new Error(`build failed directory ${directory} do not exist main file`);
}
return isExist;
}
function checkVersionExists(pkg: string, version: string, distTag: string): Promise<boolean> {
const tag = distTag || 'latest';
return getNpmInfo(pkg).then((data) => {
if (!data['dist-tags'] || (!data['dist-tags'][tag] && !data['dist-tags'].latest)) {
console.error(`${pkg} 没有 ${tag} 和 latest 版本号`, data);
return Promise.reject(new Error('Error: 没有版本号信息'));
}
// 如果该 tag 未发布,用 latest
return data['dist-tags'][tag] || data['dist-tags'].latest;
}).then((tagVersion) => version === tagVersion).catch(() => false);
}
export function getVersionPrefix(version): string {
return isNaN(version[0]) ? version[0] : '';
}
export async function getPackageInfos(distTag = ''): Promise<IPackageInfo[]> {
const packageInfos: IPackageInfo[] = [];
if (!existsSync(TARGET_DIRECTORY)) {
console.log(`[ERROR] Directory ${TARGET_DIRECTORY} not exist!`);
} else {
const packageFolders: string[] = readdirSync(TARGET_DIRECTORY)
.filter((filename) => filename[0] !== '.')
.map((packageFolder) => join(TARGET_DIRECTORY, packageFolder));
console.log('[PUBLISH] Start check with following packages:');
await Promise.all(packageFolders.map(async (packageFolder) => {
const packageInfoPath = join(packageFolder, 'package.json');
// Process package info.
if (existsSync(packageInfoPath)) {
const packageInfo = JSON.parse(readFileSync(packageInfoPath, 'utf8'));
const packageName = packageInfo.name;
const publishVersion = semver.valid(semver.coerce(packageInfo.version));
console.log(`- ${packageName}`);
try {
packageInfos.push({
name: packageName,
directory: packageFolder,
localVersion: packageInfo.version,
publishVersion,
mainFile: packageInfo.main,
packageInfo,
// If localVersion not exist, publish it
shouldPublish:
checkBuildSuccess(packageFolder, packageInfo.main) &&
!await checkVersionExists(packageName, publishVersion, distTag)
});
} catch (e) {
console.log(`[ERROR] get ${packageName} information failed: `, e);
}
} else {
console.log(`[ERROR] ${packageFolder}'s package.json not found.`);
}
}));
}
return packageInfos;
}

29
scripts/getPackages.ts Normal file
View File

@ -0,0 +1,29 @@
/* eslint no-restricted-syntax:0, no-await-in-loop:0, no-restricted-syntax:0 */
import * as path from 'path';
import * as fse from 'fs-extra';
import * as _glob from 'glob';
import * as pify from 'pify';
const glob = pify(_glob);
export default async function getPackages() {
const packageNames = [];
const packageDirs = [];
const rootPkgPath = path.join(process.cwd(), 'package.json');
const rootPkgContent = fse.readJSONSync(rootPkgPath);
for (const workspace of rootPkgContent.workspaces || []) {
const dirs = await glob(workspace);
for (const dir of dirs) {
if (fse.existsSync(path.resolve(dir, 'package.json'))) {
const pkgContent = fse.readJSONSync(path.resolve(dir, 'package.json'));
packageNames.push(pkgContent.name);
packageDirs.push(path.resolve(dir));
} else {
console.warn('Invalid workspace package:', dir);
}
}
}
return { packageNames, packageDirs };
}

40
scripts/publishPackage.ts Normal file
View File

@ -0,0 +1,40 @@
/**
* Scripts to check unpublished version and run publish
*/
import { spawnSync } from 'child_process';
import { setPublishedPackages } from './published-info';
import { IPackageInfo, getPackageInfos } from './getPackageInfos';
const publishTag = process.env.PUBLISH_TAG || '';
function publish(pkg: string, version: string, directory: string): void {
console.log('[PUBLISH]', `${pkg}@${version}`);
const npmCommand = ['publish'];
if (publishTag) {
npmCommand.push(`--tag=${publishTag}`);
}
spawnSync('npm', npmCommand, {
stdio: 'inherit',
cwd: directory,
});
}
// Entry
console.log('[PUBLISH] Start:');
getPackageInfos(publishTag).then((packageInfos: IPackageInfo[]) => {
// Publish
let publishedCount = 0;
const publishedPackages = [];
for (let i = 0; i < packageInfos.length; i++) {
const { name, directory, localVersion, shouldPublish } = packageInfos[i];
if (shouldPublish) {
publishedCount++;
console.log(`--- ${name}@${localVersion} ---`);
publish(name, localVersion, directory);
publishedPackages.push(`${name}:${localVersion}`);
}
}
console.log(`[PUBLISH PACKAGE PRODUCTION] Complete (count=${publishedCount}):`);
console.log(`${publishedPackages.join('\n')}`);
setPublishedPackages(publishedPackages);
});

View File

@ -0,0 +1,108 @@
/**
* Scripts to check unpublished version and run publish
*/
import * as path from 'path';
import * as fs from 'fs-extra';
import { spawnSync } from 'child_process';
import { setPublishedPackages } from './published-info';
import { IPackageInfo, getPackageInfos, getVersionPrefix } from './getPackageInfos';
const PUBLISH_TYPE = process.env.PUBLISH_TYPE || 'beta';
const VERSION_PREFIX = process.env.VERSION_PREFIX || PUBLISH_TYPE;
const DIST_TAG_REG = new RegExp(`([^-]+)-${VERSION_PREFIX}\\.(\\d+)`);
interface ITagPackageInfo extends IPackageInfo {
distTagVersion: string;
}
const publishTag = process.env.PUBLISH_TAG || '';
function getVersionInfo(packageInfo: IPackageInfo, tag: string): ITagPackageInfo {
const { name, localVersion } = packageInfo;
let version = localVersion;
if (!DIST_TAG_REG.test(localVersion)) {
let distTagVersion = 1;
const childProcess = spawnSync('npm', [
'show', name, 'dist-tags',
'--json',
], {
encoding: 'utf-8'
});
let distTags = {};
try {
distTags = JSON.parse(childProcess.stdout) || {};
// eslint-disable-next-line no-empty
} catch (err) {}
const matched = (distTags[tag] || '').match(DIST_TAG_REG);
// 1.0.0-beta.1 -> ["1.0.0-beta.1", "1.0.0", "1"] -> 1.0.0-beta.2
if (matched && matched[1] === localVersion && matched[2]) {
distTagVersion = Number(matched[2]) + 1;
}
version += `-${VERSION_PREFIX}.${distTagVersion}`;
}
return Object.assign({}, packageInfo, { distTagVersion: version });
}
function updatePackageJson(packageInfos: ITagPackageInfo[]): void {
packageInfos.forEach((packageInfo: ITagPackageInfo) => {
const { directory, distTagVersion } = packageInfo;
const packageFile = path.join(directory, 'package.json');
const packageData = fs.readJsonSync(packageFile);
packageData.version = distTagVersion;
for (let i = 0; i < packageInfos.length; i++) {
const dependenceName = packageInfos[i].name;
const dependenceVersion = packageInfos[i].distTagVersion;
if (packageData.dependencies && packageData.dependencies[dependenceName]) {
packageData.dependencies[dependenceName] = `${getVersionPrefix(packageData.dependencies[dependenceName])}${dependenceVersion}`;
} else if (packageData.devDependencies && packageData.devDependencies[dependenceName]) {
packageData.devDependencies[dependenceName] = `${getVersionPrefix(packageData.devDependencies[dependenceName])}${dependenceVersion}`;
}
}
fs.writeFileSync(packageFile, JSON.stringify(packageData, null, 2));
});
}
function publish(pkg: string, distTagVersion: string, directory: string, tag: string): void {
console.log(`[PUBLISH ${tag.toUpperCase()}]`, `${pkg}@${distTagVersion}`);
spawnSync('npm', [
'publish',
`--tag=${tag}`,
], {
stdio: 'inherit',
cwd: directory,
});
}
// Entry
console.log(`[PUBLISH ${PUBLISH_TYPE.toUpperCase()}] Start:`);
getPackageInfos(publishTag).then((packageInfos: IPackageInfo[]) => {
const shouldPublishPackages = packageInfos
.filter((packageInfo) => packageInfo.shouldPublish)
.map((packageInfo) => getVersionInfo(packageInfo, PUBLISH_TYPE));
updatePackageJson(shouldPublishPackages);
// Publish
let publishedCount = 0;
const publishedPackages = [];
shouldPublishPackages.forEach((packageInfo) => {
const { name, directory, distTagVersion } = packageInfo;
publishedCount++;
console.log(`--- ${name}@${distTagVersion} ---`);
publish(name, distTagVersion, directory, PUBLISH_TYPE);
publishedPackages.push(`${name}:${distTagVersion}`);
});
console.log(`[PUBLISH PACKAGE ${PUBLISH_TYPE.toUpperCase()}] Complete (count=${publishedCount}):`);
console.log(`${publishedPackages.join('\n')}`);
setPublishedPackages(publishedPackages);
});

11
scripts/publishedInfo.ts Normal file
View File

@ -0,0 +1,11 @@
import * as path from 'path';
import * as fs from 'fs-extra';
// Set and get published packages info
const PACKAGE_TEMP_FILE = 'publishedPackages.temp.json';
export function setPublishedPackages(publishedPackages: string[]): void {
fs.writeFileSync(path.join(process.cwd(), PACKAGE_TEMP_FILE), JSON.stringify(publishedPackages));
}
export function getPublishedPackages(): string[] {
return JSON.parse(fs.readFileSync(path.join(process.cwd(), PACKAGE_TEMP_FILE), 'utf-8'));
}

6
scripts/shell.ts Normal file
View File

@ -0,0 +1,6 @@
import { execaCommand } from 'execa';
export async function run(command: string) {
console.log(`[RUN]: ${command}`);
return execaCommand(command, { stdio: 'inherit' });
}

35
scripts/tagVersion.ts Normal file
View File

@ -0,0 +1,35 @@
import * as path from 'path';
import * as fs from 'fs-extra';
import * as semver from 'semver';
import { getPackageInfos, IPackageInfo, getVersionPrefix } from './getPackageInfos';
console.log('[VERSION] tag versions');
function updatePackageVersion(publishPackages: IPackageInfo[]) {
publishPackages.forEach((publishPackage: IPackageInfo) => {
const { directory, packageInfo, publishVersion, name } = publishPackage;
packageInfo.version = publishVersion;
// update package version depend on publish package
for (let i = 0; i < publishPackages.length; i++) {
const dependenceName = publishPackages[i].name;
const dependenceVersion = publishPackages[i].publishVersion;
if (packageInfo.dependencies && packageInfo.dependencies[dependenceName]) {
packageInfo.dependencies[dependenceName] = `${getVersionPrefix(packageInfo.dependencies[dependenceName])}${dependenceVersion}`;
} else if (packageInfo.devDependencies && packageInfo.devDependencies[dependenceName]) {
if (!semver.satisfies(dependenceVersion, packageInfo.devDependencies[dependenceName])) {
packageInfo.devDependencies[dependenceName] = `${getVersionPrefix(packageInfo.devDependencies[dependenceName])}${dependenceVersion}`;
}
}
}
console.log(`[VERSION] update package ${name} with version ${publishVersion}`);
fs.writeFileSync(path.join(directory, 'package.json'), JSON.stringify(packageInfo, null, 2));
});
}
getPackageInfos().then((packageInfos: IPackageInfo[]) => {
const shouldPublishPackages = packageInfos
.filter((packageInfo) => packageInfo.shouldPublish);
console.log('shouldPublishPackages', shouldPublishPackages);
updatePackageVersion(shouldPublishPackages);
});

38
scripts/versionCheck.ts Normal file
View File

@ -0,0 +1,38 @@
import * as semver from 'semver';
import { getPackageInfos, IPackageInfo } from './getPackageInfos';
function checkPackageVersion(publishPackages: IPackageInfo[]) {
publishPackages.forEach((publishPackage: IPackageInfo) => {
const { publishVersion, localVersion, name, packageInfo } = publishPackage;
if (publishVersion !== localVersion) {
throw new Error(`[ERROR] version of package ${name} is not valid local verison: ${localVersion}`);
}
for (let i = 0; i < publishPackages.length; i++) {
const dependenceName = publishPackages[i].name;
const dependenceVersion = publishPackages[i].publishVersion;
const dependencyInfo = `${dependenceName}${packageInfo.dependencies[dependenceName]}`;
if (packageInfo.dependencies && packageInfo.dependencies[dependenceName]) {
if (!semver.satisfies(dependenceVersion, packageInfo.dependencies[dependenceName])) {
throw new Error(`[ERROR] dependency ${dependencyInfo} of package ${name}
is not satisfied with verison ${dependenceVersion}`);
}
} else if (packageInfo.devDependencies && packageInfo.devDependencies[dependenceName]) {
if (!semver.satisfies(dependenceVersion, packageInfo.devDependencies[dependenceName])) {
throw new Error(`[ERROR] devDependency ${dependencyInfo} of package ${name}
is not satisfied with verison ${dependenceVersion}`);
}
}
}
});
}
getPackageInfos().then((packageInfos: IPackageInfo[]) => {
const shouldPublishPackages = packageInfos
.filter((packageInfo) => packageInfo.shouldPublish);
checkPackageVersion(shouldPublishPackages);
console.log('[VERSION] check successfully');
}).catch((e) => {
console.trace(e.message);
process.exit(128);
});

42
scripts/watch.ts Normal file
View File

@ -0,0 +1,42 @@
/* eslint @typescript-eslint/explicit-function-return-type:0, no-shadow: 0 */
import glob from 'glob';
import * as path from 'path';
import * as fs from 'fs-extra';
import * as chokidar from 'chokidar';
import { run } from './shell';
(async () => {
await run('npm run clean');
const filePatten = '*/src/**/!(*.ts|*.tsx|*.rs)';
console.log(`[COPY]: ${filePatten}`);
const cwd = path.join(process.cwd(), 'packages');
console.log('glob', glob);
const files = glob.sync(filePatten, { cwd, nodir: true });
/* eslint no-restricted-syntax:0 */
for (const file of files) {
/* eslint no-await-in-loop:0 */
await copyOneFile(file, cwd);
}
const watcher = chokidar.watch(cwd, { ignoreInitial: true });
watcher
.on('all', (event, filePath) => {
const availableEvents = ['add', 'change'];
if (availableEvents.includes(event)
&& filePath.match(/.+[\\/]src[\\/].+\.(?!ts$|tsx$|rs$)/)) {
console.log('non-ts change detected:', filePath);
copyOneFile(path.relative(cwd, filePath), cwd);
}
});
await run('npx tsc --build ./tsconfig.json -w');
})().catch((e) => {
console.trace(e);
process.exit(128);
});
async function copyOneFile(file, cwd) {
const from = path.join(cwd, file);
const to = path.join(cwd, file.replace(/\/src\//, '/lib/'));
await fs.copy(from, to);
}

123
test/utils/browser.ts Normal file
View File

@ -0,0 +1,123 @@
import * as http from 'http';
import * as url from 'url';
import * as fse from 'fs-extra';
import * as path from 'path';
import * as 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)[]>;
push?: (url: string, options?: puppeteer.WaitForOptions & { referer?: string }) => Promise<puppeteer.HTTPResponse>;
}
interface IBrowserOptions {
cwd?: string;
port?: number;
server?: http.Server;
}
export default class Browser {
private server: http.Server;
private browser: puppeteer.Browser;
private baseUrl: string;
constructor (options: IBrowserOptions) {
const { server } = options;
if (server) {
this.server = server;
} else {
const { cwd, port } = options;
this.server = this.createServer(cwd, port);
}
}
createServer(cwd: string, port: number) {
return http.createServer((req, res) => {
const requrl: string = req.url || '';
const pathname = `${cwd}${url.parse(requrl).pathname}`;
if (fse.existsSync(pathname)) {
switch (path.extname(pathname)) { // set HTTP HEAD
case '.html':
res.writeHead(200, { 'Content-Type': 'text/html' });
break;
case '.js':
res.writeHead(200, { 'Content-Type': 'text/javascript' });
break;
case '.css':
res.writeHead(200, { 'Content-Type': 'text/css' });
break;
case '.gif':
res.writeHead(200, { 'Content-Type': 'image/gif' });
break;
case '.jpg':
res.writeHead(200, { 'Content-Type': 'image/jpeg' });
break;
case '.png':
res.writeHead(200, { 'Content-Type': 'image/png' });
break;
default:
res.writeHead(200, {
'Content-Type': 'application/octet-stream',
});
}
fse.readFile(pathname, (_err, data) => {
res.end(data);
});
} else {
res.writeHead(404, { 'Content-Type': 'text/html' });
res.end('<h1>404 Not Found</h1>');
console.log(`${pathname} Not Found.`);
}
}).listen(port, '127.0.0.1');
}
async start () {
this.browser = await puppeteer.launch();
}
async close () {
if (!this.browser) { return }
await this.browser.close();
this.server.close();
}
async page (url: string) {
this.baseUrl = url;
if (!this.browser) { throw new Error('Please call start() before page(url)'); }
const page: IPage = await this.browser.newPage();
await page.goto(url);
page.push = (url, options) => page.goto(`${this.baseUrl}${url}`, options);
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
}, trim);
page.$$text = (selector, trim) =>
page.$$eval(selector, (els, trim) => els.map((el) => {
return trim ? (el.textContent || '').replace(/^\s+|\s+$/g, '') : el.textContent
}), trim);
page.$attr = (selector, attr) => {
return page.$eval(
selector,
(el: Element, ...args: unknown[]) => {
const [] = args;
return el.getAttribute(attr)
},
attr
)
};
page.$$attr = (selector, attr) =>{
return page.$$eval(
selector,
(els, ...args: unknown[]) => {
return els.map(el => el.getAttribute(attr))
},
attr
);
}
return page;
}
}

52
test/utils/build.ts Normal file
View File

@ -0,0 +1,52 @@
import * as path from 'path';
import * as process from 'process';
import { build } from 'build-scripts';
import * as getPort from 'get-port';
import Browser, { IPage } from './browser';
import getBuiltInPlugins = require('../../packages/icejs/src/getBuiltInPlugins');
interface ISetupBrowser {
(options: {
example: string;
outputDir?: string;
defaultHtml?: string;
}): Promise<IReturn>;
}
interface IReturn {
page: IPage;
browser: Browser;
}
// get builtIn plugins
export const buildFixture = function(example: string) {
test(`setup ${example}`, async () => {
const rootDir = path.join(__dirname, `../../examples/${example}`);
const processCwdSpy = jest.spyOn(process, 'cwd');
processCwdSpy.mockReturnValue(rootDir);
process.env.DISABLE_FS_CACHE = 'true';
await build({
args: {
config: path.join(rootDir, 'build.json'),
},
rootDir,
getBuiltInPlugins: (userConfig) => {
return getBuiltInPlugins(userConfig).concat(require.resolve('./test-plugin'));
},
});
}, 120000);
}
export const setupBrowser: ISetupBrowser = async (options) => {
const { example, outputDir = 'build', defaultHtml = 'index.html' } = options;
const rootDir = path.join(__dirname, `../../examples/${example}`);
const port = await getPort();
const browser = new Browser({ cwd: path.join(rootDir, outputDir), port });
await browser.start();
const page = await browser.page(`http://127.0.0.1:${port}/${defaultHtml}`);
return {
browser,
page,
}
}

View File

@ -0,0 +1,9 @@
import { spawnSync } from 'child_process';
export default (order: string, cwd: string) => {
const [command, ...args] = order.split(' ');
spawnSync(command, args, {
stdio: 'inherit',
cwd,
});
}

51
test/utils/start.ts Normal file
View File

@ -0,0 +1,51 @@
import * as path from 'path';
import { start } from 'build-scripts';
import * as getPort from 'get-port';
import Browser, { IPage } from './browser';
import { Server } from 'http';
import getBuiltInPlugins = require('../../packages/icejs/src/getBuiltInPlugins');
interface ISetupBrowser {
(options: { port: number; defaultPath?: string; server: Server; }): Promise<IReturn>;
}
interface IReturn {
page: IPage;
browser: Browser;
}
// get builtIn plugins
export const startFixture = async function (example: string) {
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 devServer = await start({
args: {
config: path.join(rootDir, 'build.json'),
port,
disableOpen: true
},
rootDir,
getBuiltInPlugins: (userConfig) => {
return getBuiltInPlugins(userConfig).concat(require.resolve('./test-plugin'));
},
}) as any as Server;
return {
port,
devServer
};
};
export const setupStartBrowser: ISetupBrowser = async (options) => {
const { port, server, defaultPath = '' } = options;
const browser = new Browser({ server });
await browser.start();
const page = await browser.page(`http://127.0.0.1:${port}/${defaultPath}`);
return {
browser,
page,
};
};

View File

@ -0,0 +1,6 @@
export default ({ modifyUserConfig }) => {
// disable minify to speed-up fixture builds
modifyUserConfig('minify', false);
// disable sourceMap to speed-up fixture start
modifyUserConfig('sourceMap', false);
}

12
tsconfig.base.json Normal file
View File

@ -0,0 +1,12 @@
{
"compilerOptions": {
"target": "ES5",
"jsx": "react",
"experimentalDecorators": true,
"declaration": true,
"sourceMap": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"allowJs": true
}
}

7
tsconfig.json Normal file
View File

@ -0,0 +1,7 @@
{
"files": [],
"references": [
{ "path": "packages/plugin-app"},
{ "path": "packages/icejs" }
]
}