init comi omip
This commit is contained in:
parent
c4fc59648a
commit
a3f8584a62
|
@ -0,0 +1,12 @@
|
|||
# http://editorconfig.org
|
||||
root = true
|
||||
|
||||
[*]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
|
||||
[*.md]
|
||||
trim_trailing_whitespace = false
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"extends": "eslint:recommended",
|
||||
"rules": {
|
||||
"no-unused-vars": ["error", { "varsIgnorePattern": "Taro" }],
|
||||
"react/jsx-filename-extension": [1, { "extensions": [".js", ".jsx", ".tsx"] }]
|
||||
},
|
||||
"parser": "babel-eslint"
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
dist/
|
||||
.temp/
|
||||
.rn_temp/
|
||||
node_modules/
|
||||
.DS_Store
|
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2019 Omi Team
|
||||
|
||||
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.
|
|
@ -0,0 +1,9 @@
|
|||
module.exports = {
|
||||
env: {
|
||||
NODE_ENV: '"development"'
|
||||
},
|
||||
defineConstants: {
|
||||
},
|
||||
weapp: {},
|
||||
h5: {}
|
||||
}
|
|
@ -0,0 +1,107 @@
|
|||
const config = {
|
||||
projectName: 'my-app',
|
||||
date: '2019-2-21',
|
||||
designWidth: 750,
|
||||
deviceRatio: {
|
||||
'640': 2.34 / 2,
|
||||
'750': 1,
|
||||
'828': 1.81 / 2
|
||||
},
|
||||
alias: {
|
||||
'omi': 'src/libs/omip-h5/omi.esm.js',
|
||||
'@tarojs': 'src/libs'
|
||||
},
|
||||
sourceRoot: 'src',
|
||||
outputRoot: 'dist',
|
||||
plugins: {
|
||||
babel: {
|
||||
sourceMap: true,
|
||||
presets: [
|
||||
['env', {
|
||||
modules: false
|
||||
}]
|
||||
],
|
||||
plugins: [
|
||||
'transform-decorators-legacy',
|
||||
'transform-class-properties',
|
||||
'transform-object-rest-spread',
|
||||
// ['transform-react-jsx', { pragma: 'global.Omi.h' }]
|
||||
]
|
||||
}
|
||||
},
|
||||
defineConstants: {
|
||||
},
|
||||
copy: {
|
||||
patterns: [
|
||||
],
|
||||
options: {
|
||||
}
|
||||
},
|
||||
weapp: {
|
||||
module: {
|
||||
postcss: {
|
||||
autoprefixer: {
|
||||
enable: true,
|
||||
config: {
|
||||
browsers: [
|
||||
'last 3 versions',
|
||||
'Android >= 4.1',
|
||||
'ios >= 8'
|
||||
]
|
||||
}
|
||||
},
|
||||
pxtransform: {
|
||||
enable: false,
|
||||
config: {
|
||||
|
||||
}
|
||||
},
|
||||
url: {
|
||||
enable: true,
|
||||
config: {
|
||||
limit: 10240 // 设定转换尺寸上限
|
||||
}
|
||||
},
|
||||
cssModules: {
|
||||
enable: false, // 默认为 false,如需使用 css modules 功能,则设为 true
|
||||
config: {
|
||||
namingPattern: 'module', // 转换模式,取值为 global/module
|
||||
generateScopedName: '[name]__[local]___[hash:base64:5]'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
h5: {
|
||||
publicPath: '/',
|
||||
staticDirectory: 'static',
|
||||
module: {
|
||||
postcss: {
|
||||
autoprefixer: {
|
||||
enable: true,
|
||||
config: {
|
||||
browsers: [
|
||||
'last 3 versions',
|
||||
'Android >= 4.1',
|
||||
'ios >= 8'
|
||||
]
|
||||
}
|
||||
},
|
||||
cssModules: {
|
||||
enable: false, // 默认为 false,如需使用 css modules 功能,则设为 true
|
||||
config: {
|
||||
namingPattern: 'module', // 转换模式,取值为 global/module
|
||||
generateScopedName: '[name]__[local]___[hash:base64:5]'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = function (merge) {
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
return merge({}, config, require('./dev'))
|
||||
}
|
||||
return merge({}, config, require('./prod'))
|
||||
}
|
|
@ -0,0 +1,107 @@
|
|||
const config = {
|
||||
projectName: 'my-app',
|
||||
date: '2019-2-21',
|
||||
designWidth: 750,
|
||||
deviceRatio: {
|
||||
'640': 2.34 / 2,
|
||||
'750': 1,
|
||||
'828': 1.81 / 2
|
||||
},
|
||||
alias: {
|
||||
'omi': 'src/libs/omip/index.js',
|
||||
'@tarojs': 'src/libs'
|
||||
},
|
||||
sourceRoot: 'src',
|
||||
outputRoot: 'dist',
|
||||
plugins: {
|
||||
babel: {
|
||||
sourceMap: true,
|
||||
presets: [
|
||||
['env', {
|
||||
modules: false
|
||||
}]
|
||||
],
|
||||
plugins: [
|
||||
'transform-decorators-legacy',
|
||||
'transform-class-properties',
|
||||
'transform-object-rest-spread',
|
||||
['transform-react-jsx', { pragma: 'global.Omi.h' }]
|
||||
]
|
||||
}
|
||||
},
|
||||
defineConstants: {
|
||||
},
|
||||
copy: {
|
||||
patterns: [
|
||||
],
|
||||
options: {
|
||||
}
|
||||
},
|
||||
weapp: {
|
||||
module: {
|
||||
postcss: {
|
||||
autoprefixer: {
|
||||
enable: true,
|
||||
config: {
|
||||
browsers: [
|
||||
'last 3 versions',
|
||||
'Android >= 4.1',
|
||||
'ios >= 8'
|
||||
]
|
||||
}
|
||||
},
|
||||
pxtransform: {
|
||||
enable: false,
|
||||
config: {
|
||||
|
||||
}
|
||||
},
|
||||
url: {
|
||||
enable: true,
|
||||
config: {
|
||||
limit: 10240 // 设定转换尺寸上限
|
||||
}
|
||||
},
|
||||
cssModules: {
|
||||
enable: false, // 默认为 false,如需使用 css modules 功能,则设为 true
|
||||
config: {
|
||||
namingPattern: 'module', // 转换模式,取值为 global/module
|
||||
generateScopedName: '[name]__[local]___[hash:base64:5]'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
h5: {
|
||||
publicPath: '/',
|
||||
staticDirectory: 'static',
|
||||
module: {
|
||||
postcss: {
|
||||
autoprefixer: {
|
||||
enable: true,
|
||||
config: {
|
||||
browsers: [
|
||||
'last 3 versions',
|
||||
'Android >= 4.1',
|
||||
'ios >= 8'
|
||||
]
|
||||
}
|
||||
},
|
||||
cssModules: {
|
||||
enable: false, // 默认为 false,如需使用 css modules 功能,则设为 true
|
||||
config: {
|
||||
namingPattern: 'module', // 转换模式,取值为 global/module
|
||||
generateScopedName: '[name]__[local]___[hash:base64:5]'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = function (merge) {
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
return merge({}, config, require('./dev'))
|
||||
}
|
||||
return merge({}, config, require('./prod'))
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
module.exports = {
|
||||
env: {
|
||||
NODE_ENV: '"production"'
|
||||
},
|
||||
defineConstants: {
|
||||
},
|
||||
weapp: {},
|
||||
h5: {
|
||||
publicPath: './',
|
||||
}
|
||||
}
|
|
@ -0,0 +1,122 @@
|
|||
{
|
||||
"name": "omip",
|
||||
"version": "1.0.0",
|
||||
"private": true,
|
||||
"description": "",
|
||||
"scripts": {
|
||||
"start": "npm run build:weapp -- --watch",
|
||||
"build:weapp": "node ./scripts/taro-cli/bin/taro build --type weapp",
|
||||
"build:swan": "node ./scripts/taro-cli/bin/taro build --type swan",
|
||||
"build:alipay": "node ./scripts/taro-cli/bin/taro build --type alipay",
|
||||
"build:tt": "node ./scripts/taro-cli/bin/taro build --type tt",
|
||||
"build:h5": "node ./scripts/taro-cli/bin/taro build --type h5",
|
||||
"build:rn": "node ./scripts/taro-cli/bin/taro build --type rn",
|
||||
"dev:swan": "npm run build:swan -- --watch",
|
||||
"dev:alipay": "npm run build:alipay -- --watch",
|
||||
"dev:tt": "npm run build:tt -- --watch",
|
||||
"dev:h5": "npm run build:h5 -- --watch",
|
||||
"dev:rn": "npm run build:rn -- --watch"
|
||||
},
|
||||
"author": "",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/code-frame": "^7.0.0-beta.44",
|
||||
"@fdaciuk/ajax": "^3.0.4",
|
||||
"@tarojs/components": "1.2.13",
|
||||
"@tarojs/router": "1.2.13",
|
||||
"@tarojs/taro": "1.2.13",
|
||||
"@tarojs/taro-alipay": "1.2.13",
|
||||
"@tarojs/taro-h5": "1.2.13",
|
||||
"@tarojs/taro-swan": "1.2.13",
|
||||
"@tarojs/taro-tt": "1.2.13",
|
||||
"@tarojs/taro-weapp": "1.2.13",
|
||||
"@tarojs/taroize": "1.2.13",
|
||||
"autoprefixer": "^8.4.1",
|
||||
"babel-core": "^6.26.3",
|
||||
"babel-eslint": "^8.2.3",
|
||||
"babel-generator": "^6.26.1",
|
||||
"babel-helper-evaluate-path": "^0.5.0",
|
||||
"babel-helper-mark-eval-scopes": "^0.4.3",
|
||||
"babel-helper-remove-or-void": "^0.4.3",
|
||||
"babel-plugin-danger-remove-unused-import": "^1.1.1",
|
||||
"babel-plugin-minify-dead-code": "^0.5.2",
|
||||
"babel-plugin-remove-dead-code": "^1.3.2",
|
||||
"babel-plugin-syntax-dynamic-import": "^6.18.0",
|
||||
"babel-plugin-transform-class-properties": "^6.24.1",
|
||||
"babel-plugin-transform-decorators-legacy": "^1.3.4",
|
||||
"babel-plugin-transform-define": "^1.3.0",
|
||||
"babel-plugin-transform-es2015-template-literals": "^6.22.0",
|
||||
"babel-plugin-transform-flow-strip-types": "^6.22.0",
|
||||
"babel-plugin-transform-jsx-to-stylesheet": "1.2.13",
|
||||
"babel-plugin-transform-react-jsx": "^6.24.1",
|
||||
"babel-template": "^6.26.0",
|
||||
"babel-traverse": "^6.26.0",
|
||||
"babel-types": "^6.26.0",
|
||||
"babylon": "^6.18.0",
|
||||
"better-babel-generator": "^6.26.1",
|
||||
"chalk": "^2.3.2",
|
||||
"chokidar": "^2.0.3",
|
||||
"commander": "^2.15.0",
|
||||
"cross-spawn": "^6.0.5",
|
||||
"css": "^2.2.4",
|
||||
"css-to-react-native-transform": "^1.4.0",
|
||||
"css-what": "^2.1.3",
|
||||
"ejs": "^2.6.1",
|
||||
"envinfo": "^6.0.1",
|
||||
"eslint": "^4.15.0",
|
||||
"eslint-plugin-taro": "1.2.13",
|
||||
"fs-extra": "^5.0.0",
|
||||
"generic-names": "^2.0.1",
|
||||
"glob": "^7.1.2",
|
||||
"html": "^1.0.0",
|
||||
"inquirer": "^5.2.0",
|
||||
"klaw": "^2.1.1",
|
||||
"latest-version": "^4.0.0",
|
||||
"lodash": "^4.17.5",
|
||||
"mem-fs": "^1.1.3",
|
||||
"mem-fs-editor": "^4.0.0",
|
||||
"minimatch": "^3.0.4",
|
||||
"nerv-devtools": "^1.3.9",
|
||||
"nervjs": "^1.3.9",
|
||||
"omi-router": "^2.0.8",
|
||||
"ora": "^2.0.0",
|
||||
"postcss": "^6.0.22",
|
||||
"postcss-modules-extract-imports": "^1.1.0",
|
||||
"postcss-modules-local-by-default": "^1.2.0",
|
||||
"postcss-modules-resolve-imports": "^1.3.0",
|
||||
"postcss-modules-scope": "^1.1.0",
|
||||
"postcss-modules-values": "^1.3.0",
|
||||
"postcss-pxtransform": "1.2.13",
|
||||
"postcss-taro-unit-transform": "1.2.13",
|
||||
"postcss-url": "^7.3.2",
|
||||
"prettier": "^1.14.3",
|
||||
"prop-types": "^15.6.2",
|
||||
"resolve": "^1.6.0",
|
||||
"semver": "^5.5.0",
|
||||
"shelljs": "^0.8.1",
|
||||
"through2": "^2.0.3",
|
||||
"typescript": "^3.2.2",
|
||||
"vinyl": "^2.1.0",
|
||||
"vinyl-fs": "^3.0.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/react": "^16.4.6",
|
||||
"@types/webpack-env": "^1.13.6",
|
||||
"@tarojs/plugin-babel": "1.2.13",
|
||||
"@tarojs/plugin-csso": "1.2.13",
|
||||
"@tarojs/plugin-uglifyjs": "1.2.13",
|
||||
"@tarojs/webpack-runner": "1.2.13",
|
||||
"babel-plugin-transform-class-properties": "^6.24.1",
|
||||
"babel-plugin-transform-decorators-legacy": "^1.3.4",
|
||||
"babel-plugin-transform-jsx-stylesheet": "^0.6.5",
|
||||
"babel-plugin-transform-object-rest-spread": "^6.26.0",
|
||||
"babel-plugin-transform-react-jsx": "^6.24.1",
|
||||
"babel-preset-env": "^1.6.1",
|
||||
"babel-eslint": "^8.2.3",
|
||||
"eslint": "^4.19.1",
|
||||
"eslint-config-taro": "1.2.13",
|
||||
"eslint-plugin-react": "^7.8.2",
|
||||
"eslint-plugin-import": "^2.12.0",
|
||||
"eslint-plugin-taro": "1.2.13"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"miniprogramRoot": "./dist",
|
||||
"projectname": "my-app",
|
||||
"description": "",
|
||||
"appid": "touristappid",
|
||||
"setting": {
|
||||
"urlCheck": true,
|
||||
"es6": false,
|
||||
"postcss": false,
|
||||
"minified": false
|
||||
},
|
||||
"compileType": "miniprogram"
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
#! /usr/bin/env node
|
||||
|
||||
const program = require('commander')
|
||||
const { getPkgVersion, printPkgVersion } = require('../src/util')
|
||||
|
||||
printPkgVersion()
|
||||
|
||||
const startTime = new Date('2019-1-1 00:00').getTime()
|
||||
const endTime = new Date('2019-1-2 00:00').getTime()
|
||||
const nowTime = Date.now()
|
||||
if (nowTime >= startTime && nowTime <= endTime) {
|
||||
const yearTxt = String(require('fs-extra').readFileSync(require('path').resolve(__dirname, 'year.txt')))
|
||||
console.log(require('chalk').rgb(255, 87, 34)(yearTxt))
|
||||
}
|
||||
|
||||
program
|
||||
.version(getPkgVersion())
|
||||
.usage('<command> [options]')
|
||||
.command('init [projectName]', 'Init a project with default templete')
|
||||
.command('build', 'Build a project with options')
|
||||
.command('update', 'Update packages of taro')
|
||||
.command('convert', 'Convert weapp to taro')
|
||||
.command('info', 'Diagnostics Taro env info')
|
||||
.parse(process.argv)
|
|
@ -0,0 +1,55 @@
|
|||
#!/usr/bin/env node
|
||||
const path = require('path')
|
||||
const fs = require('fs-extra')
|
||||
const program = require('commander')
|
||||
const chalk = require('chalk')
|
||||
const _ = require('lodash')
|
||||
|
||||
const build = require('../src/build')
|
||||
const { PROJECT_CONFIG } = require('../src/util')
|
||||
const projectConfPath = path.join(process.cwd(), PROJECT_CONFIG)
|
||||
|
||||
program
|
||||
.option('--type [typeName]', 'Build type, weapp/h5/rn/swan/alipay/tt')
|
||||
.option('--watch', 'Watch mode')
|
||||
.option('--env [env]', 'Env type')
|
||||
.option('--ui', 'Build Taro UI library')
|
||||
.parse(process.argv)
|
||||
|
||||
const args = program.args
|
||||
const { type, watch, ui } = program
|
||||
let { env } = program
|
||||
|
||||
env = process.env.NODE_ENV || env
|
||||
|
||||
if (ui) {
|
||||
console.log(chalk.green(`开始编译 UI 库`))
|
||||
build(args, {
|
||||
type: 'ui',
|
||||
watch
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
if (!fs.existsSync(projectConfPath)) {
|
||||
console.log(chalk.red(`找不到项目配置文件${PROJECT_CONFIG},请确定当前目录是Taro项目根目录!`))
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
if (env) {
|
||||
process.env.NODE_ENV = env
|
||||
} else {
|
||||
if (watch) {
|
||||
process.env.NODE_ENV = 'development'
|
||||
} else {
|
||||
process.env.NODE_ENV = 'production'
|
||||
}
|
||||
}
|
||||
|
||||
const projectConf = require(projectConfPath)(_.merge)
|
||||
console.log(chalk.green(`开始编译项目 ${chalk.bold(projectConf.projectName)}`))
|
||||
|
||||
build(args, {
|
||||
type,
|
||||
watch
|
||||
})
|
|
@ -0,0 +1,12 @@
|
|||
#!/usr/bin/env node
|
||||
|
||||
const program = require('commander')
|
||||
|
||||
const Convertor = require('../src/convertor')
|
||||
|
||||
program
|
||||
.parse(process.argv)
|
||||
|
||||
const convertor = new Convertor()
|
||||
|
||||
convertor.run()
|
|
@ -0,0 +1,53 @@
|
|||
#!/usr/bin/env node
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
const envinfo = require('envinfo')
|
||||
const {getPkgVersion, UPDATE_PACKAGE_LIST} = require('../src/util')
|
||||
const process = require('process')
|
||||
const program = require('commander')
|
||||
|
||||
const npmPackages = UPDATE_PACKAGE_LIST.concat(['react', 'react-native', 'nervjs', 'expo'])
|
||||
|
||||
program.parse(process.argv)
|
||||
|
||||
const args = program.args
|
||||
|
||||
if (args.length === 1) {
|
||||
switch (args[0]) {
|
||||
case 'rn': {
|
||||
rnInfo({
|
||||
SDKs: ['iOS SDK', 'Android SDK']
|
||||
})
|
||||
break
|
||||
}
|
||||
default:
|
||||
info()
|
||||
}
|
||||
} else {
|
||||
info()
|
||||
}
|
||||
|
||||
function rnInfo (options) {
|
||||
const appPath = process.cwd()
|
||||
const tempPath = path.join(appPath, '.rn_temp')
|
||||
if (fs.lstatSync(tempPath).isDirectory()) {
|
||||
process.chdir('.rn_temp')
|
||||
info(options)
|
||||
}
|
||||
}
|
||||
|
||||
async function info (options) {
|
||||
let info = await envinfo.run(
|
||||
{
|
||||
System: ['OS', 'Shell'],
|
||||
Binaries: ['Node', 'Yarn', 'npm'],
|
||||
npmPackages,
|
||||
npmGlobalPackages: ['typescript'],
|
||||
...options
|
||||
},
|
||||
{
|
||||
title: `Taro CLI ${getPkgVersion()} environment info`
|
||||
}
|
||||
)
|
||||
console.log(info)
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
#!/usr/bin/env node
|
||||
|
||||
const program = require('commander')
|
||||
|
||||
const Project = require('../src/project')
|
||||
|
||||
program
|
||||
.option('--name [name]', '项目名称')
|
||||
.option('--description [description]', '项目介绍')
|
||||
.option('--typescript', '使用TypeScript')
|
||||
.option('--no-typescript', '不使用TypeScript')
|
||||
.option('--template [template]', '项目模板(default/redux/mobx)')
|
||||
.option('--css [css]', 'CSS预处理器(sass/less/stylus/none)')
|
||||
.parse(process.argv)
|
||||
|
||||
const args = program.args
|
||||
const { template, description, name, css } = program
|
||||
let typescript = ''
|
||||
|
||||
/**
|
||||
* 非标准做法
|
||||
* 为了兼容不指定typescript参数时,在inquirer中询问是否使用typescript的情况
|
||||
*/
|
||||
if (program.rawArgs.indexOf('--typescript') !== -1) {
|
||||
typescript = true
|
||||
} else if (program.rawArgs.indexOf('--no-typescript') !== -1) {
|
||||
typescript = false
|
||||
}
|
||||
|
||||
const projectName = args[0] || name
|
||||
|
||||
const project = new Project({
|
||||
projectName,
|
||||
template,
|
||||
description,
|
||||
typescript,
|
||||
css
|
||||
})
|
||||
|
||||
project.create()
|
|
@ -0,0 +1,118 @@
|
|||
#!/usr/bin/env node
|
||||
const path = require('path')
|
||||
const fs = require('fs-extra')
|
||||
const program = require('commander')
|
||||
const chalk = require('chalk')
|
||||
const { getPkgItemByKey } = require('../src/util')
|
||||
const ora = require('ora')
|
||||
const exec = require('child_process').exec
|
||||
const getLatestVersion = require('latest-version')
|
||||
const { PROJECT_CONFIG, UPDATE_PACKAGE_LIST} = require('../src/util')
|
||||
const projectConfPath = path.join(process.cwd(), PROJECT_CONFIG)
|
||||
const pkgPath = path.join(process.cwd(), 'package.json')
|
||||
const { shouldUseYarn, shouldUseCnpm } = require('../src/util')
|
||||
|
||||
const pkgName = getPkgItemByKey('name')
|
||||
|
||||
// 这里没有使用 command 的形式:taro-update-self
|
||||
program.parse(process.argv)
|
||||
|
||||
const args = program.args
|
||||
|
||||
if (args.length === 1) {
|
||||
switch (args[0]) {
|
||||
case 'self': {
|
||||
updateSelf()
|
||||
break
|
||||
}
|
||||
case 'project': {
|
||||
updateProject()
|
||||
break
|
||||
}
|
||||
default:
|
||||
info()
|
||||
}
|
||||
} else {
|
||||
info()
|
||||
}
|
||||
|
||||
function info () {
|
||||
console.log(chalk.red('命令错误:'))
|
||||
console.log(`${chalk.green('taro update self')} 更新 Taro 开发工具 taro-cli 到最新版本`)
|
||||
console.log(`${chalk.green('taro update project')} 更新项目所有 Taro 相关依赖到最新版本...`)
|
||||
}
|
||||
|
||||
function updateSelf () {
|
||||
let command
|
||||
if (shouldUseCnpm()) {
|
||||
command = 'cnpm i -g @tarojs/cli@latest'
|
||||
} else {
|
||||
command = 'npm i -g @tarojs/cli@latest'
|
||||
}
|
||||
|
||||
let child = exec(command)
|
||||
|
||||
const spinner = ora('即将将 Taro 开发工具 taro-cli 更新到最新版本...').start()
|
||||
|
||||
child.stdout.on('data', function (data) {
|
||||
console.log(data)
|
||||
spinner.stop()
|
||||
})
|
||||
child.stderr.on('data', function (data) {
|
||||
console.log(data)
|
||||
spinner.stop()
|
||||
})
|
||||
}
|
||||
|
||||
async function updateProject () {
|
||||
if (!fs.existsSync(projectConfPath)) {
|
||||
console.log(chalk.red(`找不到项目配置文件${PROJECT_CONFIG},请确定当前目录是Taro项目根目录!`))
|
||||
process.exit(1)
|
||||
}
|
||||
const packageMap = require(pkgPath)
|
||||
|
||||
const version = await getLatestVersion(pkgName)
|
||||
|
||||
// 更新 @tarojs/* 版本
|
||||
Object.keys(packageMap.dependencies).forEach((key) => {
|
||||
if (UPDATE_PACKAGE_LIST.indexOf(key) !== -1) {
|
||||
packageMap.dependencies[key] = version
|
||||
}
|
||||
})
|
||||
Object.keys(packageMap.devDependencies).forEach((key) => {
|
||||
if (UPDATE_PACKAGE_LIST.indexOf(key) !== -1) {
|
||||
packageMap.devDependencies[key] = version
|
||||
}
|
||||
})
|
||||
|
||||
// 写入package.json
|
||||
try {
|
||||
await fs.writeJson(pkgPath, packageMap, {spaces: '\t'})
|
||||
console.log(chalk.green('更新项目 package.json 成功!'))
|
||||
console.log()
|
||||
} catch (err) {
|
||||
console.error(err)
|
||||
}
|
||||
|
||||
let command
|
||||
if (shouldUseYarn()) {
|
||||
command = 'yarn'
|
||||
} else if (shouldUseCnpm()) {
|
||||
command = 'cnpm install'
|
||||
} else {
|
||||
command = 'npm install'
|
||||
}
|
||||
|
||||
let child = exec(command)
|
||||
|
||||
const spinner = ora('即将将项目所有 Taro 相关依赖更新到最新版本...').start()
|
||||
|
||||
child.stdout.on('data', function (data) {
|
||||
spinner.stop()
|
||||
console.log(data)
|
||||
})
|
||||
child.stderr.on('data', function (data) {
|
||||
spinner.stop()
|
||||
console.log(data)
|
||||
})
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
🏮 🎉 🏮 🎉 🏮 🎉 🏮 🎉 🏮 🎉 🏮 🎉 🏮 🎉 🏮 🎉 🏮 🎉 🏮 🎉 🏮 🎉 🏮 🎉 🏮 🎉 🏮 🎉 🏮
|
||||
🎉 _ _ _ _ _____ _____ __ _____ _ 🎉
|
||||
🏮 | | | | | | | / __ | _ / || _ | | | 🏮
|
||||
🎉 | |_| | ___| | | ___ `' / /| |/' `| || |_| | | | 🎉
|
||||
🏮 | _ |/ _ | | |/ _ \ / / | /| || |\____ | | | 🏮
|
||||
🎉 | | | | __| | | (_) | ./ /__\ |_/ _| |.___/ / |_| 🎉
|
||||
🏮 \_| |_/\___|_|_|\___/ \_____/\___/\___\____/ (_) 🏮
|
||||
🎉 🎉
|
||||
🏮 🎉 🏮 🎉 🏮 🎉 🏮 🎉 🏮 🎉 🏮 🎉 🏮 🎉 🏮 🎉 🏮 🎉 🏮 🎉 🏮 🎉 🏮 🎉 🏮 🎉 🏮 🎉 🏮
|
|
@ -0,0 +1,87 @@
|
|||
{
|
||||
"name": "@tarojs/cli",
|
||||
"version": "1.2.13",
|
||||
"description": "cli tool for taro",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "jest"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/NervJS/taro.git"
|
||||
},
|
||||
"bin": {
|
||||
"taro": "bin/taro"
|
||||
},
|
||||
"keywords": [
|
||||
"taro",
|
||||
"weapp"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
},
|
||||
"author": "O2Team",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@tarojs/taroize": "1.2.13",
|
||||
"autoprefixer": "^8.4.1",
|
||||
"babel-core": "^6.26.3",
|
||||
"babel-generator": "^6.26.1",
|
||||
"babel-plugin-danger-remove-unused-import": "^1.1.1",
|
||||
"babel-plugin-remove-dead-code": "^1.3.2",
|
||||
"babel-plugin-transform-decorators-legacy": "^1.3.4",
|
||||
"babel-plugin-transform-define": "^1.3.0",
|
||||
"babel-plugin-transform-jsx-to-stylesheet": "1.2.13",
|
||||
"babel-plugin-transform-react-jsx": "^6.24.1",
|
||||
"babel-template": "^6.26.0",
|
||||
"babel-traverse": "^6.26.0",
|
||||
"babel-types": "^6.26.0",
|
||||
"babylon": "^6.18.0",
|
||||
"better-babel-generator": "^6.26.1",
|
||||
"chalk": "^2.3.2",
|
||||
"chokidar": "^2.0.3",
|
||||
"commander": "^2.15.0",
|
||||
"cross-spawn": "^6.0.5",
|
||||
"css-to-react-native-transform": "^1.4.0",
|
||||
"ejs": "^2.6.1",
|
||||
"envinfo": "^6.0.1",
|
||||
"fs-extra": "^5.0.0",
|
||||
"generic-names": "^2.0.1",
|
||||
"glob": "^7.1.2",
|
||||
"inquirer": "^5.2.0",
|
||||
"klaw": "^2.1.1",
|
||||
"latest-version": "^4.0.0",
|
||||
"lodash": "^4.17.5",
|
||||
"mem-fs": "^1.1.3",
|
||||
"mem-fs-editor": "^4.0.0",
|
||||
"minimatch": "^3.0.4",
|
||||
"ora": "^2.0.0",
|
||||
"postcss": "^6.0.22",
|
||||
"postcss-modules-extract-imports": "^1.1.0",
|
||||
"postcss-modules-local-by-default": "^1.2.0",
|
||||
"postcss-modules-resolve-imports": "^1.3.0",
|
||||
"postcss-modules-scope": "^1.1.0",
|
||||
"postcss-modules-values": "^1.3.0",
|
||||
"postcss-pxtransform": "1.2.13",
|
||||
"postcss-taro-unit-transform": "1.2.13",
|
||||
"postcss-url": "^7.3.2",
|
||||
"prettier": "^1.14.3",
|
||||
"prop-types": "^15.6.2",
|
||||
"resolve": "^1.6.0",
|
||||
"semver": "^5.5.0",
|
||||
"shelljs": "^0.8.1",
|
||||
"through2": "^2.0.3",
|
||||
"vinyl": "^2.1.0",
|
||||
"vinyl-fs": "^3.0.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"babel-jest": "^23.6.0",
|
||||
"babel-plugin-transform-class-properties": "^6.24.1",
|
||||
"babel-preset-env": "^1.7.0",
|
||||
"babel-preset-react": "^6.24.1",
|
||||
"babel-preset-stage-0": "^6.24.1",
|
||||
"jest": "^23.6.0",
|
||||
"jest-react-native": "^18.0.0",
|
||||
"react-native": "^0.55.4"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,90 @@
|
|||
const fs = require('fs-extra')
|
||||
const path = require('path')
|
||||
const chalk = require('chalk')
|
||||
const _ = require('lodash')
|
||||
|
||||
const Util = require('./util')
|
||||
const CONFIG = require('./config')
|
||||
|
||||
const appPath = process.cwd()
|
||||
|
||||
|
||||
function build (args, buildConfig) {
|
||||
const { type, watch } = buildConfig
|
||||
const configDir = require(path.join(appPath, Util.PROJECT_CONFIG))(_.merge)
|
||||
const outputPath = path.join(appPath, configDir.outputRoot || CONFIG.OUTPUT_DIR)
|
||||
if (!fs.existsSync(outputPath)) {
|
||||
fs.mkdirSync(outputPath)
|
||||
} else {
|
||||
if (type !== Util.BUILD_TYPES.H5) {
|
||||
Util.emptyDirectory(outputPath)
|
||||
}
|
||||
}
|
||||
switch (type) {
|
||||
case Util.BUILD_TYPES.H5:
|
||||
buildForH5({ watch })
|
||||
break
|
||||
case Util.BUILD_TYPES.WEAPP:
|
||||
buildForWeapp({ watch })
|
||||
break
|
||||
case Util.BUILD_TYPES.SWAN:
|
||||
buildForSwan({ watch })
|
||||
break
|
||||
case Util.BUILD_TYPES.ALIPAY:
|
||||
buildForAlipay({ watch })
|
||||
break
|
||||
case Util.BUILD_TYPES.TT:
|
||||
buildForTt({ watch })
|
||||
break
|
||||
case Util.BUILD_TYPES.RN:
|
||||
buildForRN({ watch })
|
||||
break
|
||||
case Util.BUILD_TYPES.UI:
|
||||
buildForUILibrary({ watch })
|
||||
break
|
||||
default:
|
||||
console.log(chalk.red('输入类型错误,目前只支持 weapp/h5/rn/swan/alipay/tt 六端类型'))
|
||||
}
|
||||
}
|
||||
|
||||
function buildForWeapp ({ watch }) {
|
||||
require('./weapp').build({
|
||||
watch,
|
||||
adapter: Util.BUILD_TYPES.WEAPP
|
||||
})
|
||||
}
|
||||
|
||||
function buildForSwan ({ watch }) {
|
||||
require('./weapp').build({
|
||||
watch,
|
||||
adapter: Util.BUILD_TYPES.SWAN
|
||||
})
|
||||
}
|
||||
|
||||
function buildForAlipay ({ watch }) {
|
||||
require('./weapp').build({
|
||||
watch,
|
||||
adapter: Util.BUILD_TYPES.ALIPAY
|
||||
})
|
||||
}
|
||||
|
||||
function buildForTt ({ watch }) {
|
||||
require('./weapp').build({
|
||||
watch,
|
||||
adapter: Util.BUILD_TYPES.TT
|
||||
})
|
||||
}
|
||||
|
||||
function buildForH5 (buildConfig) {
|
||||
require('./h5').build(buildConfig)
|
||||
}
|
||||
|
||||
function buildForRN ({ watch }) {
|
||||
require('./rn').build({ watch })
|
||||
}
|
||||
|
||||
function buildForUILibrary ({ watch }) {
|
||||
require('./ui').build({ watch })
|
||||
}
|
||||
|
||||
module.exports = build
|
|
@ -0,0 +1,12 @@
|
|||
module.exports = {
|
||||
sourceMap: true,
|
||||
presets: [
|
||||
'env'
|
||||
],
|
||||
plugins: [
|
||||
require('babel-plugin-transform-react-jsx'),
|
||||
'transform-decorators-legacy',
|
||||
'transform-class-properties',
|
||||
'transform-object-rest-spread'
|
||||
]
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
module.exports = {
|
||||
sourceType: 'module',
|
||||
plugins: [
|
||||
'typescript',
|
||||
'classProperties',
|
||||
'jsx',
|
||||
'trailingFunctionCommas',
|
||||
'asyncFunctions',
|
||||
'exponentiationOperator',
|
||||
'asyncGenerators',
|
||||
'objectRestSpread',
|
||||
'decorators',
|
||||
'dynamicImport'
|
||||
]
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
module.exports = [
|
||||
'last 3 versions',
|
||||
'Android >= 4.1',
|
||||
'ios >= 8'
|
||||
]
|
|
@ -0,0 +1,7 @@
|
|||
module.exports = {
|
||||
OUTPUT_DIR: 'dist',
|
||||
SOURCE_DIR: 'src',
|
||||
TEMP_DIR: '.temp',
|
||||
NPM_DIR: 'npm',
|
||||
ENTRY: 'app'
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"baseUrl": ".",
|
||||
"declaration": false,
|
||||
"experimentalDecorators": true,
|
||||
"jsx": "react",
|
||||
"jsxFactory": "Nerv.createElement",
|
||||
"module": "commonjs",
|
||||
"moduleResolution": "node",
|
||||
"noImplicitAny": false,
|
||||
"noUnusedLocals": true,
|
||||
"outDir": "./dist/",
|
||||
"preserveConstEnums": true,
|
||||
"removeComments": false,
|
||||
"rootDir": ".",
|
||||
"sourceMap": true,
|
||||
"strictNullChecks": true,
|
||||
"target": "es6"
|
||||
},
|
||||
"include": [
|
||||
"src/**/*"
|
||||
],
|
||||
"exclude": [
|
||||
"node_modules"
|
||||
],
|
||||
"compileOnSave": false
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
module.exports = {
|
||||
|
||||
}
|
|
@ -0,0 +1,720 @@
|
|||
const fs = require('fs-extra')
|
||||
const path = require('path')
|
||||
|
||||
const chalk = require('chalk')
|
||||
const prettier = require('prettier')
|
||||
const traverse = require('babel-traverse').default
|
||||
const t = require('babel-types')
|
||||
const template = require('babel-template')
|
||||
const taroize = require('@tarojs/taroize')
|
||||
const wxTransformer = require('../../taro-transformer-wx/lib/src/index.js').default
|
||||
const postcss = require('postcss')
|
||||
const unitTransform = require('postcss-taro-unit-transform')
|
||||
|
||||
const {
|
||||
BUILD_TYPES,
|
||||
MINI_APP_FILES,
|
||||
printLog,
|
||||
pocessTypeEnum,
|
||||
promoteRelativePath,
|
||||
resolveScriptPath,
|
||||
REG_SCRIPT,
|
||||
REG_TYPESCRIPT,
|
||||
processStyleImports,
|
||||
getPkgVersion,
|
||||
pascalCase,
|
||||
emptyDirectory,
|
||||
REG_URL,
|
||||
REG_IMAGE
|
||||
} = require('./util')
|
||||
|
||||
const { generateMinimalEscapeCode } = require('./util/ast_convert')
|
||||
|
||||
const Creator = require('./creator')
|
||||
const babylonConfig = require('./config/babylon')
|
||||
|
||||
const prettierJSConfig = {
|
||||
semi: false,
|
||||
singleQuote: true,
|
||||
parser: 'babel'
|
||||
}
|
||||
|
||||
const OUTPUT_STYLE_EXTNAME = '.scss'
|
||||
|
||||
const WX_GLOBAL_FN = ['getApp', 'getCurrentPages', 'requirePlugin']
|
||||
|
||||
function analyzeImportUrl (sourceFilePath, scriptFiles, source, value) {
|
||||
const valueExtname = path.extname(value)
|
||||
if (path.isAbsolute(value)) {
|
||||
printLog(pocessTypeEnum.ERROR, '引用文件', `文件 ${sourceFilePath} 中引用 ${value} 是绝对路径!`)
|
||||
return
|
||||
}
|
||||
if (value.indexOf('.') === 0) {
|
||||
if (REG_SCRIPT.test(valueExtname) || REG_TYPESCRIPT.test(valueExtname)) {
|
||||
const vpath = path.resolve(sourceFilePath, '..', value)
|
||||
let fPath = value
|
||||
if (fs.existsSync(vpath)) {
|
||||
fPath = vpath
|
||||
} else {
|
||||
printLog(pocessTypeEnum.ERROR, '引用文件', `文件 ${sourceFilePath} 中引用 ${value} 不存在!`)
|
||||
}
|
||||
scriptFiles.add(fPath)
|
||||
} else {
|
||||
let vpath = resolveScriptPath(path.resolve(sourceFilePath, '..', value))
|
||||
if (vpath) {
|
||||
if (!fs.existsSync(vpath)) {
|
||||
printLog(pocessTypeEnum.ERROR, '引用文件', `文件 ${sourceFilePath} 中引用 ${value} 不存在!`)
|
||||
} else {
|
||||
if (fs.lstatSync(vpath).isDirectory()) {
|
||||
if (fs.existsSync(path.join(vpath, 'index.js'))) {
|
||||
vpath = path.join(vpath, 'index.js')
|
||||
} else {
|
||||
printLog(pocessTypeEnum.ERROR, '引用目录', `文件 ${sourceFilePath} 中引用了目录 ${value}!`)
|
||||
return
|
||||
}
|
||||
}
|
||||
let relativePath = path.relative(sourceFilePath, vpath)
|
||||
const relativePathExtname = path.extname(relativePath)
|
||||
scriptFiles.add(vpath)
|
||||
relativePath = promoteRelativePath(relativePath)
|
||||
if (/\.wxs/.test(relativePathExtname)) {
|
||||
relativePath += '.js'
|
||||
} else {
|
||||
relativePath = relativePath.replace(relativePathExtname, '.js')
|
||||
}
|
||||
source.value = relativePath
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class Convertor {
|
||||
constructor () {
|
||||
this.root = process.cwd()
|
||||
this.convertRoot = path.join(this.root, 'taroConvert')
|
||||
this.convertDir = path.join(this.convertRoot, 'src')
|
||||
this.importsDir = path.join(this.convertDir, 'imports')
|
||||
this.fileTypes = MINI_APP_FILES[BUILD_TYPES.WEAPP]
|
||||
this.pages = new Set()
|
||||
this.components = new Set()
|
||||
this.hadBeenCopyedFiles = new Set()
|
||||
this.hadBeenBuiltComponents = new Set()
|
||||
this.hadBeenBuiltImports = new Set()
|
||||
this.init()
|
||||
}
|
||||
|
||||
init () {
|
||||
console.log(chalk.green('开始代码转换...'))
|
||||
this.initConvert()
|
||||
this.getApp()
|
||||
this.getPages()
|
||||
this.getSubPackages()
|
||||
}
|
||||
|
||||
initConvert () {
|
||||
if (fs.existsSync(this.convertRoot)) {
|
||||
emptyDirectory(this.convertRoot, { excludes: ['node_modules'] })
|
||||
} else {
|
||||
fs.mkdirpSync(this.convertRoot)
|
||||
}
|
||||
}
|
||||
|
||||
parseAst ({ ast, sourceFilePath, outputFilePath, importStylePath, depComponents, imports = [], isApp = false }) {
|
||||
const scriptFiles = new Set()
|
||||
const self = this
|
||||
let componentClassName = null
|
||||
let needInsertImportTaro = false
|
||||
traverse(ast, {
|
||||
Program: {
|
||||
enter (astPath) {
|
||||
astPath.traverse({
|
||||
ClassDeclaration (astPath) {
|
||||
const node = astPath.node
|
||||
let isTaroComponent = false
|
||||
if (node.superClass) {
|
||||
astPath.traverse({
|
||||
ClassMethod (astPath) {
|
||||
if (astPath.get('key').isIdentifier({ name: 'render' })) {
|
||||
astPath.traverse({
|
||||
JSXElement () {
|
||||
isTaroComponent = true
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
if (isTaroComponent) {
|
||||
componentClassName = node.id.name
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
ClassExpression (astPath) {
|
||||
const node = astPath.node
|
||||
if (node.superClass) {
|
||||
let isTaroComponent = false
|
||||
astPath.traverse({
|
||||
ClassMethod (astPath) {
|
||||
if (astPath.get('key').isIdentifier({ name: 'render' })) {
|
||||
astPath.traverse({
|
||||
JSXElement () {
|
||||
isTaroComponent = true
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
if (isTaroComponent) {
|
||||
if (node.id === null) {
|
||||
const parentNode = astPath.parentPath.node
|
||||
if (t.isVariableDeclarator(astPath.parentPath)) {
|
||||
componentClassName = parentNode.id.name
|
||||
}
|
||||
} else {
|
||||
componentClassName = node.id.name
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
ExportDefaultDeclaration (astPath) {
|
||||
const node = astPath.node
|
||||
const declaration = node.declaration
|
||||
if (
|
||||
declaration &&
|
||||
(declaration.type === 'ClassDeclaration' || declaration.type === 'ClassExpression')
|
||||
) {
|
||||
const superClass = declaration.superClass
|
||||
if (superClass) {
|
||||
let isTaroComponent = false
|
||||
astPath.traverse({
|
||||
ClassMethod (astPath) {
|
||||
if (astPath.get('key').isIdentifier({ name: 'render' })) {
|
||||
astPath.traverse({
|
||||
JSXElement () {
|
||||
isTaroComponent = true
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
if (isTaroComponent) {
|
||||
componentClassName = declaration.id.name
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
ImportDeclaration (astPath) {
|
||||
const node = astPath.node
|
||||
const source = node.source
|
||||
const value = source.value
|
||||
analyzeImportUrl(sourceFilePath, scriptFiles, source, value)
|
||||
},
|
||||
CallExpression (astPath) {
|
||||
const node = astPath.node
|
||||
const calleePath = astPath.get('callee')
|
||||
const callee = calleePath.node
|
||||
if (callee.type === 'Identifier') {
|
||||
if (callee.name === 'require') {
|
||||
const args = node.arguments
|
||||
const value = args[0].value
|
||||
analyzeImportUrl(sourceFilePath, scriptFiles, args[0], value)
|
||||
} else if (WX_GLOBAL_FN.includes(callee.name)) {
|
||||
calleePath.replaceWith(
|
||||
t.memberExpression(t.identifier('Taro'), callee)
|
||||
)
|
||||
needInsertImportTaro = true
|
||||
}
|
||||
} else if (callee.type === 'MemberExpression') {
|
||||
const object = callee.object
|
||||
if (object.name === 'wx') {
|
||||
calleePath.get('object').replaceWith(t.identifier('Taro'))
|
||||
needInsertImportTaro = true
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
exit (astPath) {
|
||||
const lastImport = astPath.get('body').filter(p => p.isImportDeclaration()).pop()
|
||||
const hasTaroImport = astPath.get('body').some(p => p.isImportDeclaration() && p.node.source.value === '@tarojs/taro')
|
||||
if (needInsertImportTaro && !hasTaroImport) {
|
||||
astPath.node.body.unshift(
|
||||
t.importDeclaration(
|
||||
[t.importDefaultSpecifier(t.identifier('Taro'))],
|
||||
t.stringLiteral('@tarojs/taro')
|
||||
)
|
||||
)
|
||||
}
|
||||
astPath.traverse({
|
||||
StringLiteral (astPath) {
|
||||
const value = astPath.node.value
|
||||
const extname = path.extname(value)
|
||||
if (extname && REG_IMAGE.test(extname) && !REG_URL.test(value)) {
|
||||
let imageRelativePath = null
|
||||
let sourceImagePath = null
|
||||
let outputImagePath = null
|
||||
if (path.isAbsolute(value)) {
|
||||
sourceImagePath = path.join(self.root, value)
|
||||
} else {
|
||||
sourceImagePath = path.resolve(sourceFilePath, '..', value)
|
||||
}
|
||||
imageRelativePath = promoteRelativePath(path.relative(sourceFilePath, sourceImagePath))
|
||||
outputImagePath = self.getDistFilePath(sourceImagePath)
|
||||
if (fs.existsSync(sourceImagePath)) {
|
||||
self.copyFileToTaro(sourceImagePath, outputImagePath)
|
||||
printLog(pocessTypeEnum.COPY, '图片', self.generateShowPath(outputImagePath))
|
||||
} else {
|
||||
printLog(pocessTypeEnum.ERROR, '图片不存在', self.generateShowPath(sourceImagePath))
|
||||
}
|
||||
if (astPath.parentPath.isVariableDeclarator()) {
|
||||
astPath.replaceWith(t.callExpression(t.identifier('require'), [t.stringLiteral(imageRelativePath)]))
|
||||
} else if (astPath.parentPath.isJSXAttribute()) {
|
||||
astPath.replaceWith(t.jSXExpressionContainer(t.callExpression(t.identifier('require'), [t.stringLiteral(imageRelativePath)])))
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
if (lastImport) {
|
||||
if (importStylePath) {
|
||||
lastImport.insertAfter(t.importDeclaration([], t.stringLiteral(promoteRelativePath(path.relative(sourceFilePath, importStylePath)))))
|
||||
}
|
||||
if (imports && imports.length) {
|
||||
imports.forEach(({ name, ast }) => {
|
||||
const importName = pascalCase(name)
|
||||
if (componentClassName === importName) {
|
||||
return
|
||||
}
|
||||
const importPath = path.join(self.importsDir, importName + '.js')
|
||||
if (!self.hadBeenBuiltImports.has(importPath)) {
|
||||
self.hadBeenBuiltImports.add(importPath)
|
||||
self.writeFileToTaro(importPath, prettier.format(generateMinimalEscapeCode(ast), prettierJSConfig))
|
||||
}
|
||||
lastImport.insertAfter(template(`import ${importName} from '${promoteRelativePath(path.relative(outputFilePath, importPath))}'`, babylonConfig)())
|
||||
})
|
||||
}
|
||||
if (depComponents && depComponents.size) {
|
||||
depComponents.forEach(componentObj => {
|
||||
const name = pascalCase(componentObj.name)
|
||||
const component = componentObj.path
|
||||
lastImport.insertAfter(template(`import ${name} from '${promoteRelativePath(path.relative(sourceFilePath, component))}'`, babylonConfig)())
|
||||
})
|
||||
}
|
||||
|
||||
if (isApp) {
|
||||
astPath.node.body.push(template(`Taro.render(<App />, document.getElementById('app'))`, babylonConfig)())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
return {
|
||||
ast,
|
||||
scriptFiles
|
||||
}
|
||||
}
|
||||
|
||||
getApp () {
|
||||
this.entryJSPath = path.join(this.root, `app${this.fileTypes.SCRIPT}`)
|
||||
this.entryJSONPath = path.join(this.root, `app${this.fileTypes.CONFIG}`)
|
||||
this.entryStylePath = path.join(this.root, `app${this.fileTypes.STYLE}`)
|
||||
try {
|
||||
this.entryJSON = JSON.parse(String(fs.readFileSync(this.entryJSONPath)))
|
||||
printLog(pocessTypeEnum.CONVERT, '入口文件', this.generateShowPath(this.entryJSPath))
|
||||
printLog(pocessTypeEnum.CONVERT, '入口配置', this.generateShowPath(this.entryJSONPath))
|
||||
if (fs.existsSync(this.entryStylePath)) {
|
||||
this.entryStyle = String(fs.readFileSync(this.entryStylePath))
|
||||
printLog(pocessTypeEnum.CONVERT, '入口样式', this.generateShowPath(this.entryStylePath))
|
||||
}
|
||||
} catch (err) {
|
||||
this.entryJSON = {}
|
||||
console.log(chalk.red(`app${this.fileTypes.CONFIG} 读取失败,请检查!`))
|
||||
process.exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
getPages () {
|
||||
const pages = this.entryJSON['pages']
|
||||
if (!pages || !pages.length) {
|
||||
console.log(chalk.red(`app${this.fileTypes.CONFIG} 配置有误,缺少页面相关配置`))
|
||||
return
|
||||
}
|
||||
this.pages = new Set(pages)
|
||||
}
|
||||
|
||||
getSubPackages () {
|
||||
const subPackages = this.entryJSON['subpackages'] || this.entryJSON['subPackages']
|
||||
if (!subPackages || !subPackages.length) {
|
||||
return
|
||||
}
|
||||
subPackages.forEach(item => {
|
||||
if (item.pages && item.pages.length) {
|
||||
const root = item.root
|
||||
item.pages.forEach(page => {
|
||||
let pagePath = `${root}/${page}`
|
||||
pagePath = pagePath.replace(/\/{2,}/g, '/')
|
||||
this.pages.add(pagePath)
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
generateScriptFiles (files) {
|
||||
if (!files) {
|
||||
return
|
||||
}
|
||||
if (files.size) {
|
||||
files.forEach(file => {
|
||||
if (!fs.existsSync(file) || this.hadBeenCopyedFiles.has(file)) {
|
||||
return
|
||||
}
|
||||
const code = fs.readFileSync(file).toString()
|
||||
let outputFilePath = file.replace(this.root, this.convertDir)
|
||||
const extname = path.extname(outputFilePath)
|
||||
if (/\.wxs/.test(extname)) {
|
||||
outputFilePath += '.js'
|
||||
}
|
||||
const transformResult = wxTransformer({
|
||||
code,
|
||||
sourcePath: file,
|
||||
outputPath: outputFilePath,
|
||||
isNormal: true,
|
||||
isTyped: REG_TYPESCRIPT.test(file)
|
||||
})
|
||||
const { ast, scriptFiles } = this.parseAst({
|
||||
ast: transformResult.ast,
|
||||
outputFilePath,
|
||||
sourceFilePath: file
|
||||
})
|
||||
const jsCode = generateMinimalEscapeCode(ast)
|
||||
this.writeFileToTaro(outputFilePath, prettier.format(jsCode, prettierJSConfig))
|
||||
printLog(pocessTypeEnum.COPY, 'JS 文件', this.generateShowPath(outputFilePath))
|
||||
this.hadBeenCopyedFiles.add(file)
|
||||
this.generateScriptFiles(scriptFiles)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
writeFileToTaro (dist, code) {
|
||||
fs.ensureDirSync(path.dirname(dist))
|
||||
fs.writeFileSync(dist, code)
|
||||
}
|
||||
|
||||
copyFileToTaro (from, to, options) {
|
||||
const filename = path.basename(from)
|
||||
if (fs.statSync(from).isFile() && !path.extname(to)) {
|
||||
fs.ensureDir(to)
|
||||
return fs.copySync(from, path.join(to, filename), options)
|
||||
}
|
||||
fs.ensureDir(path.dirname(to))
|
||||
return fs.copySync(from, to, options)
|
||||
}
|
||||
|
||||
getDistFilePath (src, extname) {
|
||||
if (!extname) return src.replace(this.root, this.convertDir)
|
||||
return src.replace(this.root, this.convertDir).replace(path.extname(src), extname)
|
||||
}
|
||||
|
||||
generateShowPath (filePath) {
|
||||
return filePath.replace(path.join(this.root, '/'), '').split(path.sep).join('/')
|
||||
}
|
||||
|
||||
generateEntry () {
|
||||
try {
|
||||
const entryJS = String(fs.readFileSync(this.entryJSPath))
|
||||
const entryJSON = JSON.stringify(this.entryJSON)
|
||||
const entryDistJSPath = this.getDistFilePath(this.entryJSPath)
|
||||
const taroizeResult = taroize({
|
||||
json: entryJSON,
|
||||
script: entryJS,
|
||||
path: path.dirname(entryJS)
|
||||
})
|
||||
const { ast, scriptFiles } = this.parseAst({
|
||||
ast: taroizeResult.ast,
|
||||
sourceFilePath: this.entryJSPath,
|
||||
outputFilePath: entryDistJSPath,
|
||||
importStylePath: this.entryStyle ? this.entryStylePath.replace(path.extname(this.entryStylePath), OUTPUT_STYLE_EXTNAME) : null,
|
||||
isApp: true
|
||||
})
|
||||
const jsCode = generateMinimalEscapeCode(ast)
|
||||
this.writeFileToTaro(entryDistJSPath, prettier.format(jsCode, prettierJSConfig))
|
||||
printLog(pocessTypeEnum.GENERATE, '入口文件', this.generateShowPath(entryDistJSPath))
|
||||
if (this.entryStyle) {
|
||||
this.traverseStyle(this.entryStylePath, this.entryStyle)
|
||||
}
|
||||
this.generateScriptFiles(scriptFiles)
|
||||
if (this.entryJSON.tabBar) {
|
||||
this.generateTabBarIcon(this.entryJSON.tabBar)
|
||||
}
|
||||
} catch (err) {
|
||||
console.log(err)
|
||||
}
|
||||
}
|
||||
|
||||
generateTabBarIcon (tabBar) {
|
||||
const { list = [] } = tabBar
|
||||
const icons = new Set()
|
||||
if (Array.isArray(list) && list.length) {
|
||||
list.forEach(item => {
|
||||
if (typeof item.iconPath === 'string') icons.add(item.iconPath)
|
||||
if (typeof item.selectedIconPath === 'string') icons.add(item.selectedIconPath)
|
||||
})
|
||||
if (icons.size > 0) {
|
||||
Array.from(icons)
|
||||
.map(icon => path.join(this.root, icon))
|
||||
.forEach(iconPath => {
|
||||
const iconDistPath = this.getDistFilePath(iconPath)
|
||||
this.copyFileToTaro(iconPath, iconDistPath)
|
||||
printLog(pocessTypeEnum.COPY, 'TabBar 图标', this.generateShowPath(iconDistPath))
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
traversePages () {
|
||||
this.pages.forEach(page => {
|
||||
const pagePath = path.join(this.root, page)
|
||||
const pageJSPath = pagePath + this.fileTypes.SCRIPT
|
||||
const pageDistJSPath = this.getDistFilePath(pageJSPath)
|
||||
const pageConfigPath = pagePath + this.fileTypes.CONFIG
|
||||
const pageStylePath = pagePath + this.fileTypes.STYLE
|
||||
const pageTemplPath = pagePath + this.fileTypes.TEMPL
|
||||
|
||||
try {
|
||||
const param = {}
|
||||
const depComponents = new Set()
|
||||
if (!fs.existsSync(pageJSPath)) {
|
||||
throw new Error(`页面 ${page} 没有 JS 文件!`)
|
||||
}
|
||||
printLog(pocessTypeEnum.CONVERT, '页面文件', this.generateShowPath(pageJSPath))
|
||||
|
||||
if (fs.existsSync(pageConfigPath)) {
|
||||
printLog(pocessTypeEnum.CONVERT, '页面配置', this.generateShowPath(pageConfigPath))
|
||||
const pageConfigStr = String(fs.readFileSync(pageConfigPath))
|
||||
const pageConfig = JSON.parse(pageConfigStr)
|
||||
const pageUsingComponnets = pageConfig.usingComponents
|
||||
if (pageUsingComponnets) {
|
||||
// 页面依赖组件
|
||||
Object.keys(pageUsingComponnets).forEach(component => {
|
||||
let componentPath = path.resolve(pageConfigPath, '..', pageUsingComponnets[component])
|
||||
if (!fs.existsSync(resolveScriptPath(componentPath))) {
|
||||
componentPath = path.join(this.root, pageUsingComponnets[component])
|
||||
}
|
||||
depComponents.add({
|
||||
name: component,
|
||||
path: componentPath
|
||||
})
|
||||
})
|
||||
delete pageConfig.usingComponents
|
||||
}
|
||||
param.json = JSON.stringify(pageConfig)
|
||||
}
|
||||
param.script = String(fs.readFileSync(pageJSPath))
|
||||
if (fs.existsSync(pageTemplPath)) {
|
||||
printLog(pocessTypeEnum.CONVERT, '页面模板', this.generateShowPath(pageTemplPath))
|
||||
param.wxml = String(fs.readFileSync(pageTemplPath))
|
||||
}
|
||||
let pageStyle = null
|
||||
if (fs.existsSync(pageStylePath)) {
|
||||
printLog(pocessTypeEnum.CONVERT, '页面样式', this.generateShowPath(pageStylePath))
|
||||
pageStyle = String(fs.readFileSync(pageStylePath))
|
||||
}
|
||||
param.path = path.dirname(pageJSPath)
|
||||
const taroizeResult = taroize(param)
|
||||
const { ast, scriptFiles } = this.parseAst({
|
||||
ast: taroizeResult.ast,
|
||||
sourceFilePath: pageJSPath,
|
||||
outputFilePath: pageDistJSPath,
|
||||
importStylePath: pageStyle ? pageStylePath.replace(path.extname(pageStylePath), OUTPUT_STYLE_EXTNAME) : null,
|
||||
depComponents,
|
||||
imports: taroizeResult.imports
|
||||
})
|
||||
const jsCode = generateMinimalEscapeCode(ast)
|
||||
this.writeFileToTaro(pageDistJSPath, prettier.format(jsCode, prettierJSConfig))
|
||||
printLog(pocessTypeEnum.GENERATE, '页面文件', this.generateShowPath(pageDistJSPath))
|
||||
if (pageStyle) {
|
||||
this.traverseStyle(pageStylePath, pageStyle)
|
||||
}
|
||||
this.generateScriptFiles(scriptFiles)
|
||||
this.traverseComponents(depComponents)
|
||||
} catch (err) {
|
||||
printLog(pocessTypeEnum.ERROR, '页面转换', this.generateShowPath(pageJSPath))
|
||||
console.log(err)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
traverseComponents (components) {
|
||||
if (!components || !components.size) {
|
||||
return
|
||||
}
|
||||
components.forEach(componentObj => {
|
||||
const component = componentObj.path
|
||||
if (this.hadBeenBuiltComponents.has(component)) return
|
||||
this.hadBeenBuiltComponents.add(component)
|
||||
|
||||
const componentJSPath = component + this.fileTypes.SCRIPT
|
||||
const componentDistJSPath = this.getDistFilePath(componentJSPath)
|
||||
const componentConfigPath = component + this.fileTypes.CONFIG
|
||||
const componentStylePath = component + this.fileTypes.STYLE
|
||||
const componentTemplPath = component + this.fileTypes.TEMPL
|
||||
|
||||
try {
|
||||
const param = {}
|
||||
const depComponents = new Set()
|
||||
if (!fs.existsSync(componentJSPath)) {
|
||||
throw new Error(`组件 ${component} 没有 JS 文件!`)
|
||||
}
|
||||
printLog(pocessTypeEnum.CONVERT, '组件文件', this.generateShowPath(componentJSPath))
|
||||
if (fs.existsSync(componentConfigPath)) {
|
||||
printLog(pocessTypeEnum.CONVERT, '组件配置', this.generateShowPath(componentConfigPath))
|
||||
const componentConfigStr = String(fs.readFileSync(componentConfigPath))
|
||||
const componentConfig = JSON.parse(componentConfigStr)
|
||||
const componentUsingComponnets = componentConfig.usingComponents
|
||||
if (componentUsingComponnets) {
|
||||
// 页面依赖组件
|
||||
Object.keys(componentUsingComponnets).forEach(component => {
|
||||
let componentPath = path.resolve(componentConfigPath, '..', componentUsingComponnets[component])
|
||||
if (!fs.existsSync(resolveScriptPath(componentPath))) {
|
||||
componentPath = path.join(this.root, componentUsingComponnets[component])
|
||||
}
|
||||
depComponents.add({
|
||||
name: component,
|
||||
path: componentPath
|
||||
})
|
||||
})
|
||||
delete componentConfig.usingComponents
|
||||
}
|
||||
param.json = JSON.stringify(componentConfig)
|
||||
}
|
||||
param.script = String(fs.readFileSync(componentJSPath))
|
||||
if (fs.existsSync(componentTemplPath)) {
|
||||
printLog(pocessTypeEnum.CONVERT, '组件模板', this.generateShowPath(componentTemplPath))
|
||||
param.wxml = String(fs.readFileSync(componentTemplPath))
|
||||
}
|
||||
let componentStyle = null
|
||||
if (fs.existsSync(componentStylePath)) {
|
||||
printLog(pocessTypeEnum.CONVERT, '组件样式', this.generateShowPath(componentStylePath))
|
||||
componentStyle = String(fs.readFileSync(componentStylePath))
|
||||
}
|
||||
param.path = path.dirname(componentJSPath)
|
||||
const taroizeResult = taroize(param)
|
||||
const { ast, scriptFiles } = this.parseAst({
|
||||
ast: taroizeResult.ast,
|
||||
sourceFilePath: componentJSPath,
|
||||
outputFilePath: componentDistJSPath,
|
||||
importStylePath: componentStyle ? componentStylePath.replace(path.extname(componentStylePath), OUTPUT_STYLE_EXTNAME) : null,
|
||||
depComponents,
|
||||
imports: taroizeResult.imports
|
||||
})
|
||||
const jsCode = generateMinimalEscapeCode(ast)
|
||||
this.writeFileToTaro(componentDistJSPath, prettier.format(jsCode, prettierJSConfig))
|
||||
printLog(pocessTypeEnum.GENERATE, '组件文件', this.generateShowPath(componentDistJSPath))
|
||||
if (componentStyle) {
|
||||
this.traverseStyle(componentStylePath, componentStyle)
|
||||
}
|
||||
this.generateScriptFiles(scriptFiles)
|
||||
this.traverseComponents(depComponents)
|
||||
} catch (err) {
|
||||
printLog(pocessTypeEnum.ERROR, '组件转换', this.generateShowPath(componentJSPath))
|
||||
console.log(err)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
async styleUnitTransform (filePath, content) {
|
||||
const postcssResult = await postcss([
|
||||
unitTransform()
|
||||
]).process(content, {
|
||||
from: filePath
|
||||
})
|
||||
return postcssResult
|
||||
}
|
||||
|
||||
async traverseStyle (filePath, style) {
|
||||
const { imports, content } = processStyleImports(style, BUILD_TYPES.WEAPP, (str, stylePath) => {
|
||||
let relativePath = stylePath
|
||||
if (path.isAbsolute(relativePath)) {
|
||||
relativePath = promoteRelativePath(path.relative(filePath, path.join(this.root, stylePath)))
|
||||
}
|
||||
return str.replace(stylePath, relativePath)
|
||||
.replace(MINI_APP_FILES[BUILD_TYPES.WEAPP].STYLE, OUTPUT_STYLE_EXTNAME)
|
||||
})
|
||||
const styleDist = this.getDistFilePath(filePath, OUTPUT_STYLE_EXTNAME)
|
||||
const { css } = await this.styleUnitTransform(filePath, content)
|
||||
this.writeFileToTaro(styleDist, css)
|
||||
printLog(pocessTypeEnum.GENERATE, '样式文件', this.generateShowPath(styleDist))
|
||||
if (imports && imports.length) {
|
||||
imports.forEach(importItem => {
|
||||
const importPath = path.isAbsolute(importItem)
|
||||
? path.join(this.root, importItem)
|
||||
: path.resolve(path.dirname(filePath), importItem)
|
||||
if (fs.existsSync(importPath)) {
|
||||
const styleText = fs.readFileSync(importPath).toString()
|
||||
this.traverseStyle(importPath, styleText)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
generateConfigFiles () {
|
||||
const creator = new Creator()
|
||||
const templateName = 'default'
|
||||
const configDir = path.join(this.convertRoot, 'config')
|
||||
const pkgPath = path.join(this.convertRoot, 'package.json')
|
||||
const projectName = 'taroConvert'
|
||||
const description = ''
|
||||
const version = getPkgVersion()
|
||||
const dateObj = new Date()
|
||||
const date = `${dateObj.getFullYear()}-${(dateObj.getMonth() + 1)}-${dateObj.getDate()}`
|
||||
creator.template(templateName, 'pkg', pkgPath, {
|
||||
description,
|
||||
projectName,
|
||||
version,
|
||||
css: 'sass',
|
||||
typescript: false
|
||||
})
|
||||
creator.template(templateName, path.join('config', 'index'), path.join(configDir, 'index.js'), {
|
||||
date,
|
||||
projectName
|
||||
})
|
||||
creator.template(templateName, path.join('config', 'dev'), path.join(configDir, 'dev.js'))
|
||||
creator.template(templateName, path.join('config', 'prod'), path.join(configDir, 'prod.js'))
|
||||
creator.template(templateName, 'project', path.join(this.convertRoot, 'project.config.json'), {
|
||||
description,
|
||||
projectName
|
||||
})
|
||||
creator.template(templateName, 'gitignore', path.join(this.convertRoot, '.gitignore'))
|
||||
creator.template(templateName, 'editorconfig', path.join(this.convertRoot, '.editorconfig'))
|
||||
creator.template(templateName, 'eslintrc', path.join(this.convertRoot, '.eslintrc'), {
|
||||
typescript: false
|
||||
})
|
||||
creator.template(templateName, 'indexhtml', path.join(this.convertDir, 'index.html'))
|
||||
creator.fs.commit(() => {
|
||||
const pkgObj = JSON.parse(fs.readFileSync(pkgPath).toString())
|
||||
pkgObj.dependencies['@tarojs/with-weapp'] = `^${version}`
|
||||
fs.writeJSONSync(pkgPath, pkgObj, {
|
||||
spaces: 2,
|
||||
EOL: '\n'
|
||||
})
|
||||
printLog(pocessTypeEnum.GENERATE, '文件', this.generateShowPath(path.join(configDir, 'index.js')))
|
||||
printLog(pocessTypeEnum.GENERATE, '文件', this.generateShowPath(path.join(configDir, 'dev.js')))
|
||||
printLog(pocessTypeEnum.GENERATE, '文件', this.generateShowPath(path.join(configDir, 'prod.js')))
|
||||
printLog(pocessTypeEnum.GENERATE, '文件', this.generateShowPath(pkgPath))
|
||||
printLog(pocessTypeEnum.GENERATE, '文件', this.generateShowPath(path.join(this.convertRoot, 'project.config.json')))
|
||||
printLog(pocessTypeEnum.GENERATE, '文件', this.generateShowPath(path.join(this.convertRoot, '.gitignore')))
|
||||
printLog(pocessTypeEnum.GENERATE, '文件', this.generateShowPath(path.join(this.convertRoot, '.editorconfig')))
|
||||
printLog(pocessTypeEnum.GENERATE, '文件', this.generateShowPath(path.join(this.convertRoot, '.eslintrc')))
|
||||
printLog(pocessTypeEnum.GENERATE, '文件', this.generateShowPath(path.join(this.convertDir, 'index.html')))
|
||||
})
|
||||
}
|
||||
|
||||
run () {
|
||||
this.generateEntry()
|
||||
this.traversePages()
|
||||
this.generateConfigFiles()
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Convertor
|
|
@ -0,0 +1,86 @@
|
|||
const path = require('path')
|
||||
const fs = require('fs-extra')
|
||||
const memFs = require('mem-fs')
|
||||
const editor = require('mem-fs-editor')
|
||||
|
||||
const {
|
||||
getRootPath
|
||||
} = require('./util')
|
||||
|
||||
class Creator {
|
||||
constructor () {
|
||||
const store = memFs.create()
|
||||
this.fs = editor.create(store)
|
||||
this.sourceRoot(path.join(getRootPath()))
|
||||
this.init()
|
||||
}
|
||||
|
||||
init () {}
|
||||
|
||||
sourceRoot (rootPath) {
|
||||
if (typeof rootPath === 'string') {
|
||||
this._rootPath = path.resolve(rootPath)
|
||||
}
|
||||
if (!fs.existsSync(this._rootPath)) {
|
||||
fs.ensureDirSync(this._rootPath)
|
||||
}
|
||||
return this._rootPath
|
||||
}
|
||||
|
||||
templatePath () {
|
||||
let filepath = path.join.apply(path, arguments)
|
||||
if (!path.isAbsolute(filepath)) {
|
||||
filepath = path.join(this._rootPath, 'templates', filepath)
|
||||
}
|
||||
return filepath
|
||||
}
|
||||
|
||||
destinationRoot (rootPath) {
|
||||
if (typeof rootPath === 'string') {
|
||||
this._destinationRoot = path.resolve(rootPath)
|
||||
if (!fs.existsSync(rootPath)) {
|
||||
fs.ensureDirSync(rootPath)
|
||||
}
|
||||
process.chdir(rootPath)
|
||||
}
|
||||
return this._destinationRoot || process.cwd()
|
||||
}
|
||||
|
||||
destinationPath () {
|
||||
let filepath = path.join.apply(path, arguments)
|
||||
if (!path.isAbsolute(filepath)) {
|
||||
filepath = path.join(this.destinationRoot(), filepath)
|
||||
}
|
||||
return filepath
|
||||
}
|
||||
|
||||
template (template, source, dest, data, options) {
|
||||
if (typeof dest !== 'string') {
|
||||
options = data
|
||||
data = dest
|
||||
dest = source
|
||||
}
|
||||
this.fs.copyTpl(
|
||||
this.templatePath(template, source),
|
||||
this.destinationPath(dest),
|
||||
Object.assign({}, this, data),
|
||||
options
|
||||
)
|
||||
return this
|
||||
}
|
||||
|
||||
copy (template, type, source, dest) {
|
||||
dest = dest || source
|
||||
this.template(template, type, source, dest)
|
||||
return this
|
||||
}
|
||||
|
||||
writeGitKeepFile (dirname) {
|
||||
dirname = path.resolve(dirname)
|
||||
fs.writeFileSync(path.join(dirname, '.gitkeep'), 'Place hold file', 'utf8')
|
||||
}
|
||||
|
||||
write () {}
|
||||
}
|
||||
|
||||
module.exports = Creator
|
|
@ -0,0 +1,58 @@
|
|||
|
||||
var actionTypes = {
|
||||
"equals": "",
|
||||
"element": "~",
|
||||
"start": "^",
|
||||
"end": "$",
|
||||
"any": "*",
|
||||
"not": "!",
|
||||
"hyphen": "|"
|
||||
};
|
||||
|
||||
var simpleSelectors = {
|
||||
__proto__: null,
|
||||
child: " > ",
|
||||
parent: " < ",
|
||||
sibling: " ~ ",
|
||||
adjacent: " + ",
|
||||
descendant: " ",
|
||||
universal: "*"
|
||||
};
|
||||
|
||||
|
||||
|
||||
function stringify(token){
|
||||
return token.map(stringifySubselector).join(", ");
|
||||
}
|
||||
|
||||
function stringifySubselector(token){
|
||||
return token.map(stringifyToken).join("");
|
||||
}
|
||||
|
||||
function stringifyToken(token){
|
||||
if(token.type in simpleSelectors) return simpleSelectors[token.type];
|
||||
|
||||
if(token.type === "tag") return escapeName(token.name);
|
||||
|
||||
if(token.type === "attribute"){
|
||||
if(token.action === "exists") return "[" + escapeName(token.name) + "]";
|
||||
if(token.name === "id" && token.action === "equals" && !token.ignoreCase) return "#" + escapeName(token.value);
|
||||
if(token.name === "class" && token.action === "element" && !token.ignoreCase) return "." + escapeName(token.value);
|
||||
return "[" +
|
||||
escapeName(token.name) + actionTypes[token.action] + "='" +
|
||||
escapeName(token.value) + "'" + (token.ignoreCase ? "i" : "") + "]";
|
||||
}
|
||||
|
||||
if(token.type === "pseudo"){
|
||||
if(token.data === null) return ":" + escapeName(token.name);
|
||||
if(typeof token.data === "string") return ":" + escapeName(token.name) + "(" + token.data + ")";
|
||||
return ":" + escapeName(token.name) + "(" + stringify(token.data) + ")";
|
||||
}
|
||||
}
|
||||
|
||||
function escapeName(str){
|
||||
//TODO
|
||||
return str;
|
||||
}
|
||||
|
||||
module.exports = stringify
|
|
@ -0,0 +1,28 @@
|
|||
|
||||
let map = require('./tag-mapping')
|
||||
let css = require('css')
|
||||
let cssWhat = require('css-what')
|
||||
let cssStringify = require('./css-stringify')
|
||||
|
||||
function compileWxss(str) {
|
||||
let obj = css.parse(str)
|
||||
obj.stylesheet.rules.forEach(rule => {
|
||||
rule.selectors && rule.selectors.forEach((selector, index) => {
|
||||
let sltObjs = cssWhat(selector)
|
||||
sltObjs.forEach(sltObj => {
|
||||
sltObj.forEach(item => {
|
||||
if (item.type == 'tag') {
|
||||
item.name = map(item.name)
|
||||
|
||||
}
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
rule.selectors[index] = cssStringify(sltObjs)
|
||||
})
|
||||
})
|
||||
return css.stringify(obj)
|
||||
}
|
||||
|
||||
module.exports = compileWxss
|
|
@ -0,0 +1,19 @@
|
|||
let tagMapping = {
|
||||
view: 'div',
|
||||
text: 'span',
|
||||
image: 'img',
|
||||
picker: 'select',
|
||||
navigator: 'a',
|
||||
}
|
||||
|
||||
function map(key) {
|
||||
if (key.indexOf('-') !== -1) {
|
||||
return key
|
||||
}
|
||||
if(tagMapping[key]){
|
||||
return tagMapping[key]
|
||||
}
|
||||
return key
|
||||
}
|
||||
|
||||
module.exports = map
|
|
@ -0,0 +1,11 @@
|
|||
var assign = function (s, d) {
|
||||
if (typeof s !== 'object') {
|
||||
return d
|
||||
}
|
||||
s = s || {}
|
||||
d = d || {}
|
||||
return JSON.parse((JSON.stringify(s) + JSON.stringify(d)).replace('}{', ','))
|
||||
}
|
||||
module.exports = {
|
||||
assign: assign
|
||||
};
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,173 @@
|
|||
const path = require('path')
|
||||
const fs = require('fs-extra')
|
||||
const chalk = require('chalk')
|
||||
const inquirer = require('inquirer')
|
||||
const semver = require('semver')
|
||||
|
||||
const Creator = require('./creator')
|
||||
|
||||
const {
|
||||
shouldUseYarn,
|
||||
shouldUseCnpm,
|
||||
getPkgVersion
|
||||
} = require('./util')
|
||||
const { SOURCE_DIR } = require('./config')
|
||||
|
||||
class Project extends Creator {
|
||||
constructor (options) {
|
||||
super()
|
||||
const unSupportedVer = semver.lt(process.version, 'v7.6.0')
|
||||
if (unSupportedVer) {
|
||||
throw new Error('Node.js 版本过低,推荐升级 Node.js 至 v8.0.0+')
|
||||
}
|
||||
this.rootPath = this._rootPath
|
||||
|
||||
this.conf = Object.assign({
|
||||
projectName: null,
|
||||
template: null,
|
||||
description: ''
|
||||
}, options)
|
||||
}
|
||||
|
||||
init () {
|
||||
console.log(chalk.green(`Taro即将创建一个新项目!`))
|
||||
console.log('Need help? Go and open issue: https://github.com/NervJS/taro/issues/new')
|
||||
console.log()
|
||||
}
|
||||
|
||||
create () {
|
||||
this.ask()
|
||||
.then(answers => {
|
||||
const date = new Date()
|
||||
this.conf = Object.assign(this.conf, answers)
|
||||
this.conf.date = `${date.getFullYear()}-${(date.getMonth() + 1)}-${date.getDate()}`
|
||||
this.write()
|
||||
})
|
||||
}
|
||||
|
||||
ask () {
|
||||
const prompts = []
|
||||
const conf = this.conf
|
||||
if (typeof conf.projectName !== 'string') {
|
||||
prompts.push({
|
||||
type: 'input',
|
||||
name: 'projectName',
|
||||
message: '请输入项目名称!',
|
||||
validate (input) {
|
||||
if (!input) {
|
||||
return '项目名不能为空!'
|
||||
}
|
||||
if (fs.existsSync(input)) {
|
||||
return '当前目录已经存在同名项目,请换一个项目名!'
|
||||
}
|
||||
return true
|
||||
}
|
||||
})
|
||||
} else if (fs.existsSync(conf.projectName)) {
|
||||
prompts.push({
|
||||
type: 'input',
|
||||
name: 'projectName',
|
||||
message: '当前目录已经存在同名项目,请换一个项目名!',
|
||||
validate (input) {
|
||||
if (!input) {
|
||||
return '项目名不能为空!'
|
||||
}
|
||||
if (fs.existsSync(input)) {
|
||||
return '项目名依然重复!'
|
||||
}
|
||||
return true
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
if (typeof conf.description !== 'string') {
|
||||
prompts.push({
|
||||
type: 'input',
|
||||
name: 'description',
|
||||
message: '请输入项目介绍!'
|
||||
})
|
||||
}
|
||||
|
||||
if (typeof conf.typescript !== 'boolean') {
|
||||
prompts.push({
|
||||
type: 'confirm',
|
||||
name: 'typescript',
|
||||
message: '是否需要使用 TypeScript ?'
|
||||
})
|
||||
}
|
||||
|
||||
const cssChoices = [{
|
||||
name: 'Sass',
|
||||
value: 'sass'
|
||||
}, {
|
||||
name: 'Less',
|
||||
value: 'less'
|
||||
}, {
|
||||
name: 'Stylus',
|
||||
value: 'stylus'
|
||||
}, {
|
||||
name: '无',
|
||||
value: 'none'
|
||||
}]
|
||||
|
||||
if (typeof conf.css !== 'string') {
|
||||
prompts.push({
|
||||
type: 'list',
|
||||
name: 'css',
|
||||
message: '请选择 CSS 预处理器(Sass/Less/Stylus)',
|
||||
choices: cssChoices
|
||||
})
|
||||
}
|
||||
|
||||
const templateChoices = [{
|
||||
name: '默认模板',
|
||||
value: 'default'
|
||||
}, {
|
||||
name: 'Redux 模板',
|
||||
value: 'redux'
|
||||
}, {
|
||||
name: 'Mobx 模板',
|
||||
value: 'mobx'
|
||||
}]
|
||||
|
||||
if (typeof conf.template !== 'string') {
|
||||
prompts.push({
|
||||
type: 'list',
|
||||
name: 'template',
|
||||
message: '请选择模板',
|
||||
choices: templateChoices
|
||||
})
|
||||
} else {
|
||||
let isTemplateExist = false
|
||||
templateChoices.forEach(item => {
|
||||
if (item.value === conf.template) {
|
||||
isTemplateExist = true
|
||||
}
|
||||
})
|
||||
if (!isTemplateExist) {
|
||||
console.log(chalk.red('你选择的模板不存在!'))
|
||||
console.log(chalk.red('目前提供了以下模板以供使用:'))
|
||||
console.log()
|
||||
templateChoices.forEach(item => {
|
||||
console.log(chalk.green(`- ${item.name}`))
|
||||
})
|
||||
process.exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
return inquirer.prompt(prompts)
|
||||
}
|
||||
|
||||
write (cb) {
|
||||
const { template } = this.conf
|
||||
this.conf.src = SOURCE_DIR
|
||||
const templateCreate = require(path.join(this.templatePath(), template, 'index.js'))
|
||||
templateCreate(this, this.conf, {
|
||||
shouldUseYarn,
|
||||
shouldUseCnpm,
|
||||
getPkgVersion
|
||||
}, cb)
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Project
|
|
@ -0,0 +1,249 @@
|
|||
const fs = require('fs-extra')
|
||||
const path = require('path')
|
||||
const {performance} = require('perf_hooks')
|
||||
const chokidar = require('chokidar')
|
||||
const chalk = require('chalk')
|
||||
const ejs = require('ejs')
|
||||
const _ = require('lodash')
|
||||
const shelljs = require('shelljs')
|
||||
const klaw = require('klaw')
|
||||
|
||||
const Util = require('./util')
|
||||
const npmProcess = require('./util/npm')
|
||||
const CONFIG = require('./config')
|
||||
const {getPkgVersion} = require('./util')
|
||||
const StyleProcess = require('./rn/styleProcess')
|
||||
const {transformJSCode} = require('./rn/transformJS')
|
||||
|
||||
const appPath = process.cwd()
|
||||
const projectConfig = require(path.join(appPath, Util.PROJECT_CONFIG))(_.merge)
|
||||
const sourceDirName = projectConfig.sourceRoot || CONFIG.SOURCE_DIR
|
||||
const sourceDir = path.join(appPath, sourceDirName)
|
||||
const tempDir = '.rn_temp'
|
||||
const tempPath = path.join(appPath, tempDir)
|
||||
const entryFilePath = Util.resolveScriptPath(path.join(sourceDir, CONFIG.ENTRY))
|
||||
const entryFileName = path.basename(entryFilePath)
|
||||
const pluginsConfig = projectConfig.plugins || {}
|
||||
|
||||
const pkgPath = path.join(__dirname, './rn/pkg')
|
||||
|
||||
let depTree = {}
|
||||
|
||||
let isBuildingStyles = {}
|
||||
const styleDenpendencyTree = {}
|
||||
|
||||
function isEntryFile (filePath) {
|
||||
return path.basename(filePath) === entryFileName
|
||||
}
|
||||
|
||||
function compileDepStyles (filePath, styleFiles) {
|
||||
if (isBuildingStyles[filePath] || styleFiles.length === 0) {
|
||||
return Promise.resolve({})
|
||||
}
|
||||
isBuildingStyles[filePath] = true
|
||||
return Promise.all(styleFiles.map(async p => { // to css string
|
||||
const filePath = path.join(p)
|
||||
const fileExt = path.extname(filePath)
|
||||
Util.printLog(Util.pocessTypeEnum.COMPILE, _.camelCase(fileExt).toUpperCase(), filePath)
|
||||
return StyleProcess.loadStyle({filePath, pluginsConfig})
|
||||
})).then(resList => { // postcss
|
||||
return Promise.all(resList.map(item => {
|
||||
return StyleProcess.postCSS({...item, projectConfig})
|
||||
}))
|
||||
}).then(resList => {
|
||||
let styleObjectEntire = {}
|
||||
resList.forEach(item => {
|
||||
let styleObject = StyleProcess.getStyleObject({css: item.css, filePath: item.filePath})
|
||||
// validate styleObject
|
||||
StyleProcess.validateStyle({styleObject, filePath: item.filePath})
|
||||
|
||||
Object.assign(styleObjectEntire, styleObject)
|
||||
if (filePath !== entryFilePath) { // 非入口文件,合并全局样式
|
||||
Object.assign(styleObjectEntire, _.get(styleDenpendencyTree, [entryFilePath, 'styleObjectEntire'], {}))
|
||||
}
|
||||
styleDenpendencyTree[filePath] = {
|
||||
styleFiles,
|
||||
styleObjectEntire
|
||||
}
|
||||
})
|
||||
return JSON.stringify(styleObjectEntire, null, 2)
|
||||
}).then(css => {
|
||||
let tempFilePath = filePath.replace(sourceDir, tempPath)
|
||||
const basename = path.basename(tempFilePath, path.extname(tempFilePath))
|
||||
tempFilePath = path.join(path.dirname(tempFilePath), `${basename}_styles.js`)
|
||||
|
||||
StyleProcess.writeStyleFile({css, tempFilePath})
|
||||
}).catch((e) => {
|
||||
throw new Error(e)
|
||||
})
|
||||
}
|
||||
|
||||
function initProjectFile () {
|
||||
// generator app.json
|
||||
const appJsonObject = Object.assign({}, {
|
||||
expo: {
|
||||
sdkVersion: '27.0.0'
|
||||
}
|
||||
}, projectConfig.rn && projectConfig.rn.appJson)
|
||||
// generator .${tempPath}/package.json TODO JSON.parse 这种写法可能会有隐患
|
||||
const pkgTempObj = JSON.parse(
|
||||
ejs.render(
|
||||
fs.readFileSync(pkgPath, 'utf-8'), {
|
||||
projectName: projectConfig.projectName,
|
||||
version: getPkgVersion()
|
||||
}
|
||||
).replace(/(\r\n|\n|\r|\s+)/gm, '')
|
||||
)
|
||||
const dependencies = require(path.join(process.cwd(), 'package.json')).dependencies
|
||||
pkgTempObj.dependencies = Object.assign({}, pkgTempObj.dependencies, dependencies)
|
||||
// Copy bin/crna-entry.js ?
|
||||
const crnaEntryPath = path.join(path.dirname(npmProcess.resolveNpmSync('@tarojs/rn-runner')), 'src/bin/crna-entry.js')
|
||||
|
||||
fs.writeFileSync(path.join(tempDir, 'app.json'), JSON.stringify(appJsonObject, null, 2))
|
||||
Util.printLog(Util.pocessTypeEnum.GENERATE, 'app.json', path.join(tempPath, 'app.json'))
|
||||
fs.writeFileSync(path.join(tempDir, 'package.json'), JSON.stringify(pkgTempObj, null, 2))
|
||||
Util.printLog(Util.pocessTypeEnum.GENERATE, 'package.json', path.join(tempPath, 'package.json'))
|
||||
fs.copySync(crnaEntryPath, path.join(tempDir, 'bin/crna-entry.js'))
|
||||
Util.printLog(Util.pocessTypeEnum.COPY, 'crna-entry.js', path.join(tempPath, 'bin/crna-entry.js'))
|
||||
}
|
||||
|
||||
async function processFile (filePath) {
|
||||
if (!fs.existsSync(filePath)) {
|
||||
return
|
||||
}
|
||||
const dirname = path.dirname(filePath)
|
||||
const distDirname = dirname.replace(sourceDir, tempDir)
|
||||
let distPath = path.format({dir: distDirname, base: path.basename(filePath)})
|
||||
const code = fs.readFileSync(filePath, 'utf-8')
|
||||
if (Util.REG_STYLE.test(filePath)) {
|
||||
// do something
|
||||
} else if (Util.REG_SCRIPTS.test(filePath)) {
|
||||
if (Util.REG_TYPESCRIPT.test(filePath)) {
|
||||
distPath = distPath.replace(/\.(tsx|ts)(\?.*)?$/, '.js')
|
||||
}
|
||||
Util.printLog(Util.pocessTypeEnum.COMPILE, _.camelCase(path.extname(filePath)).toUpperCase(), filePath)
|
||||
// transformJSCode
|
||||
let transformResult = transformJSCode({code, filePath, isEntryFile: isEntryFile(filePath), projectConfig})
|
||||
const jsCode = transformResult.code
|
||||
fs.ensureDirSync(distDirname)
|
||||
fs.writeFileSync(distPath, Buffer.from(jsCode))
|
||||
// compileDepStyles
|
||||
const styleFiles = transformResult.styleFiles
|
||||
depTree[filePath] = styleFiles
|
||||
await compileDepStyles(filePath, styleFiles)
|
||||
} else {
|
||||
fs.ensureDirSync(distDirname)
|
||||
fs.copySync(filePath, distPath)
|
||||
Util.printLog(Util.pocessTypeEnum.COPY, _.camelCase(path.extname(filePath)).toUpperCase(), filePath)
|
||||
}
|
||||
}
|
||||
|
||||
function buildTemp () {
|
||||
fs.ensureDirSync(path.join(tempPath, 'bin'))
|
||||
return new Promise((resolve, reject) => {
|
||||
klaw(sourceDir)
|
||||
.on('data', file => {
|
||||
if (!file.stats.isDirectory()) {
|
||||
processFile(file.path)
|
||||
}
|
||||
})
|
||||
.on('end', () => {
|
||||
initProjectFile()
|
||||
if (!fs.existsSync(path.join(tempPath, 'node_modules'))) {
|
||||
console.log()
|
||||
console.log(chalk.yellow('开始安装依赖~'))
|
||||
process.chdir(tempPath)
|
||||
let command
|
||||
if (Util.shouldUseYarn()) {
|
||||
command = 'yarn'
|
||||
} else if (Util.shouldUseCnpm()) {
|
||||
command = 'cnpm install'
|
||||
} else {
|
||||
command = 'npm install'
|
||||
}
|
||||
shelljs.exec(command, {silent: false})
|
||||
}
|
||||
resolve()
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
async function buildDist ({watch}) {
|
||||
const entry = {
|
||||
app: path.join(tempPath, entryFileName)
|
||||
}
|
||||
const rnConfig = projectConfig.rn || {}
|
||||
rnConfig.env = projectConfig.env
|
||||
rnConfig.defineConstants = projectConfig.defineConstants
|
||||
rnConfig.designWidth = projectConfig.designWidth
|
||||
rnConfig.entry = entry
|
||||
if (watch) {
|
||||
rnConfig.isWatch = true
|
||||
}
|
||||
rnConfig.projectDir = tempPath
|
||||
const rnRunner = await npmProcess.getNpmPkg('@tarojs/rn-runner')
|
||||
rnRunner(rnConfig)
|
||||
}
|
||||
|
||||
async function perfWrap (callback, args) {
|
||||
isBuildingStyles = {} // 清空
|
||||
// 后期可以优化,不编译全部
|
||||
let t0 = performance.now()
|
||||
await callback(args)
|
||||
let t1 = performance.now()
|
||||
Util.printLog(Util.pocessTypeEnum.COMPILE, `编译完成,花费${Math.round(t1 - t0)} ms`)
|
||||
}
|
||||
|
||||
function watchFiles () {
|
||||
const watcher = chokidar.watch(path.join(sourceDir), {
|
||||
ignored: /(^|[/\\])\../,
|
||||
persistent: true,
|
||||
ignoreInitial: true
|
||||
})
|
||||
|
||||
watcher
|
||||
.on('ready', () => {
|
||||
console.log()
|
||||
console.log(chalk.gray('初始化完毕,监听文件修改中...'))
|
||||
console.log()
|
||||
})
|
||||
.on('add', filePath => {
|
||||
const relativePath = path.relative(appPath, filePath)
|
||||
Util.printLog(Util.pocessTypeEnum.CREATE, '添加文件', relativePath)
|
||||
perfWrap(buildTemp)
|
||||
})
|
||||
.on('change', filePath => {
|
||||
const relativePath = path.relative(appPath, filePath)
|
||||
Util.printLog(Util.pocessTypeEnum.MODIFY, '文件变动', relativePath)
|
||||
if (Util.REG_SCRIPTS.test(filePath)) {
|
||||
perfWrap(processFile, filePath)
|
||||
}
|
||||
if (Util.REG_STYLE.test(filePath)) {
|
||||
_.forIn(depTree, (styleFiles, jsFilePath) => {
|
||||
if (styleFiles.indexOf(filePath) > -1) {
|
||||
perfWrap(processFile, jsFilePath)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
.on('unlink', filePath => {
|
||||
const relativePath = path.relative(appPath, filePath)
|
||||
Util.printLog(Util.pocessTypeEnum.UNLINK, '删除文件', relativePath)
|
||||
perfWrap(buildTemp)
|
||||
})
|
||||
.on('error', error => console.log(`Watcher error: ${error}`))
|
||||
}
|
||||
|
||||
async function build ({watch}) {
|
||||
fs.ensureDirSync(tempPath)
|
||||
let t0 = performance.now()
|
||||
await buildTemp()
|
||||
let t1 = performance.now()
|
||||
Util.printLog(Util.pocessTypeEnum.COMPILE, `编译完成,花费${Math.round(t1 - t0)} ms`)
|
||||
await buildDist({watch})
|
||||
if (watch) {
|
||||
watchFiles()
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {build}
|
|
@ -0,0 +1,76 @@
|
|||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @format
|
||||
*/
|
||||
|
||||
'use strict'
|
||||
|
||||
const normalizeColor = require('./normalizeColor')
|
||||
|
||||
const colorPropType = function (
|
||||
isRequired,
|
||||
props,
|
||||
propName,
|
||||
componentName,
|
||||
location,
|
||||
propFullName
|
||||
) {
|
||||
const color = props[propName]
|
||||
if (color === undefined || color === null) {
|
||||
if (isRequired) {
|
||||
return new Error(
|
||||
'Required ' +
|
||||
location +
|
||||
' `' +
|
||||
(propFullName || propName) +
|
||||
'` was not specified in `' +
|
||||
componentName +
|
||||
'`.'
|
||||
)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if (typeof color === 'number') {
|
||||
// Developers should not use a number, but we are using the prop type
|
||||
// both for user provided colors and for transformed ones. This isn't ideal
|
||||
// and should be fixed but will do for now...
|
||||
return
|
||||
}
|
||||
|
||||
if (normalizeColor(color) === null) {
|
||||
return new Error(
|
||||
'Invalid ' +
|
||||
location +
|
||||
' `' +
|
||||
(propFullName || propName) +
|
||||
'` supplied to `' +
|
||||
componentName +
|
||||
'`: ' +
|
||||
color +
|
||||
'\n' +
|
||||
`Valid color formats are
|
||||
- '#f0f' (#rgb)
|
||||
- '#f0fc' (#rgba)
|
||||
- '#ff00ff' (#rrggbb)
|
||||
- '#ff00ff00' (#rrggbbaa)
|
||||
- 'rgb(255, 255, 255)'
|
||||
- 'rgba(255, 255, 255, 1.0)'
|
||||
- 'hsl(360, 100%, 100%)'
|
||||
- 'hsla(360, 100%, 100%, 1.0)'
|
||||
- 'transparent'
|
||||
- 'red'
|
||||
- 0xff00ff00 (0xrrggbbaa)
|
||||
`
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const ColorPropType = colorPropType.bind(null, false /* isRequired */)
|
||||
ColorPropType.isRequired = colorPropType.bind(null, true /* isRequired */)
|
||||
|
||||
module.exports = ColorPropType
|
|
@ -0,0 +1,52 @@
|
|||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
*
|
||||
* @format
|
||||
*/
|
||||
'use strict'
|
||||
|
||||
/* $FlowFixMe(>=0.54.0 site=react_native_oss) This comment suppresses an error
|
||||
* found when Flow v0.54 was deployed. To see the error delete this comment and
|
||||
* run Flow. */
|
||||
const keyMirror = require('fbjs/lib/keyMirror')
|
||||
|
||||
/**
|
||||
* ImageResizeMode - Enum for different image resizing modes, set via
|
||||
* `resizeMode` style property on `<Image>` components.
|
||||
*/
|
||||
const ImageResizeMode = keyMirror({
|
||||
/**
|
||||
* contain - The image will be resized such that it will be completely
|
||||
* visible, contained within the frame of the View.
|
||||
*/
|
||||
contain: null,
|
||||
/**
|
||||
* cover - The image will be resized such that the entire area of the view
|
||||
* is covered by the image, potentially clipping parts of the image.
|
||||
*/
|
||||
cover: null,
|
||||
/**
|
||||
* stretch - The image will be stretched to fill the entire frame of the
|
||||
* view without clipping. This may change the aspect ratio of the image,
|
||||
* distorting it.
|
||||
*/
|
||||
stretch: null,
|
||||
/**
|
||||
* center - The image will be scaled down such that it is completely visible,
|
||||
* if bigger than the area of the view.
|
||||
* The image will not be scaled up.
|
||||
*/
|
||||
center: null,
|
||||
|
||||
/**
|
||||
* repeat - The image will be repeated to cover the frame of the View. The
|
||||
* image will keep it's size and aspect ratio.
|
||||
*/
|
||||
repeat: null
|
||||
})
|
||||
|
||||
module.exports = ImageResizeMode
|
|
@ -0,0 +1,62 @@
|
|||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
*
|
||||
* @format
|
||||
*/
|
||||
'use strict'
|
||||
|
||||
const ColorPropType = require('./ColorPropType')
|
||||
const ImageResizeMode = require('./ImageResizeMode')
|
||||
const LayoutPropTypes = require('./LayoutPropTypes')
|
||||
const ReactPropTypes = require('prop-types')
|
||||
const ShadowPropTypesIOS = require('./ShadowPropTypesIOS')
|
||||
const TransformPropTypes = require('./TransformPropTypes')
|
||||
|
||||
const ImageStylePropTypes = {
|
||||
...LayoutPropTypes,
|
||||
...ShadowPropTypesIOS,
|
||||
...TransformPropTypes,
|
||||
resizeMode: ReactPropTypes.oneOf(Object.keys(ImageResizeMode)),
|
||||
backfaceVisibility: ReactPropTypes.oneOf(['visible', 'hidden']),
|
||||
backgroundColor: ColorPropType,
|
||||
borderColor: ColorPropType,
|
||||
borderWidth: ReactPropTypes.number,
|
||||
borderRadius: ReactPropTypes.number,
|
||||
overflow: ReactPropTypes.oneOf(['visible', 'hidden']),
|
||||
|
||||
/**
|
||||
* Changes the color of all the non-transparent pixels to the tintColor.
|
||||
*/
|
||||
tintColor: ColorPropType,
|
||||
opacity: ReactPropTypes.number,
|
||||
/**
|
||||
* When the image has rounded corners, specifying an overlayColor will
|
||||
* cause the remaining space in the corners to be filled with a solid color.
|
||||
* This is useful in cases which are not supported by the Android
|
||||
* implementation of rounded corners:
|
||||
* - Certain resize modes, such as 'contain'
|
||||
* - Animated GIFs
|
||||
*
|
||||
* A typical way to use this prop is with images displayed on a solid
|
||||
* background and setting the `overlayColor` to the same color
|
||||
* as the background.
|
||||
*
|
||||
* For details of how this works under the hood, see
|
||||
* http://frescolib.org/docs/rounded-corners-and-circles.html
|
||||
*
|
||||
* @platform android
|
||||
*/
|
||||
overlayColor: ReactPropTypes.string,
|
||||
|
||||
// Android-Specific styles
|
||||
borderTopLeftRadius: ReactPropTypes.number,
|
||||
borderTopRightRadius: ReactPropTypes.number,
|
||||
borderBottomLeftRadius: ReactPropTypes.number,
|
||||
borderBottomRightRadius: ReactPropTypes.number
|
||||
}
|
||||
|
||||
module.exports = ImageStylePropTypes
|
|
@ -0,0 +1,561 @@
|
|||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @format
|
||||
* strict
|
||||
*/
|
||||
|
||||
'use strict'
|
||||
|
||||
const ReactPropTypes = require('prop-types')
|
||||
|
||||
/**
|
||||
* React Native's layout system is based on Flexbox and is powered both
|
||||
* on iOS and Android by an open source project called `Yoga`:
|
||||
* https://github.com/facebook/yoga
|
||||
*
|
||||
* The implementation in Yoga is slightly different from what the
|
||||
* Flexbox spec defines - for example, we chose more sensible default
|
||||
* values. Since our layout docs are generated from the comments in this
|
||||
* file, please keep a brief comment describing each prop type.
|
||||
*
|
||||
* These properties are a subset of our styles that are consumed by the layout
|
||||
* algorithm and affect the positioning and sizing of views.
|
||||
*/
|
||||
const LayoutPropTypes = {
|
||||
/** `display` sets the display type of this component.
|
||||
*
|
||||
* It works similarly to `display` in CSS, but only support 'flex' and 'none'.
|
||||
* 'flex' is the default.
|
||||
*/
|
||||
display: ReactPropTypes.oneOf(['none', 'flex']),
|
||||
|
||||
/** `width` sets the width of this component.
|
||||
*
|
||||
* It works similarly to `width` in CSS, but in React Native you
|
||||
* must use points or percentages. Ems and other units are not supported.
|
||||
* See https://developer.mozilla.org/en-US/docs/Web/CSS/width for more details.
|
||||
*/
|
||||
width: ReactPropTypes.oneOfType([
|
||||
ReactPropTypes.number,
|
||||
ReactPropTypes.string
|
||||
]),
|
||||
|
||||
/** `height` sets the height of this component.
|
||||
*
|
||||
* It works similarly to `height` in CSS, but in React Native you
|
||||
* must use points or percentages. Ems and other units are not supported.
|
||||
* See https://developer.mozilla.org/en-US/docs/Web/CSS/height for more details.
|
||||
*/
|
||||
height: ReactPropTypes.oneOfType([
|
||||
ReactPropTypes.number,
|
||||
ReactPropTypes.string
|
||||
]),
|
||||
|
||||
/**
|
||||
* When the direction is `ltr`, `start` is equivalent to `left`.
|
||||
* When the direction is `rtl`, `start` is equivalent to `right`.
|
||||
*
|
||||
* This style takes precedence over the `left`, `right`, and `end` styles.
|
||||
*/
|
||||
start: ReactPropTypes.oneOfType([
|
||||
ReactPropTypes.number,
|
||||
ReactPropTypes.string
|
||||
]),
|
||||
|
||||
/**
|
||||
* When the direction is `ltr`, `end` is equivalent to `right`.
|
||||
* When the direction is `rtl`, `end` is equivalent to `left`.
|
||||
*
|
||||
* This style takes precedence over the `left` and `right` styles.
|
||||
*/
|
||||
end: ReactPropTypes.oneOfType([ReactPropTypes.number, ReactPropTypes.string]),
|
||||
|
||||
/** `top` is the number of logical pixels to offset the top edge of
|
||||
* this component.
|
||||
*
|
||||
* It works similarly to `top` in CSS, but in React Native you
|
||||
* must use points or percentages. Ems and other units are not supported.
|
||||
*
|
||||
* See https://developer.mozilla.org/en-US/docs/Web/CSS/top
|
||||
* for more details of how `top` affects layout.
|
||||
*/
|
||||
top: ReactPropTypes.oneOfType([ReactPropTypes.number, ReactPropTypes.string]),
|
||||
|
||||
/** `left` is the number of logical pixels to offset the left edge of
|
||||
* this component.
|
||||
*
|
||||
* It works similarly to `left` in CSS, but in React Native you
|
||||
* must use points or percentages. Ems and other units are not supported.
|
||||
*
|
||||
* See https://developer.mozilla.org/en-US/docs/Web/CSS/left
|
||||
* for more details of how `left` affects layout.
|
||||
*/
|
||||
left: ReactPropTypes.oneOfType([
|
||||
ReactPropTypes.number,
|
||||
ReactPropTypes.string
|
||||
]),
|
||||
|
||||
/** `right` is the number of logical pixels to offset the right edge of
|
||||
* this component.
|
||||
*
|
||||
* It works similarly to `right` in CSS, but in React Native you
|
||||
* must use points or percentages. Ems and other units are not supported.
|
||||
*
|
||||
* See https://developer.mozilla.org/en-US/docs/Web/CSS/right
|
||||
* for more details of how `right` affects layout.
|
||||
*/
|
||||
right: ReactPropTypes.oneOfType([
|
||||
ReactPropTypes.number,
|
||||
ReactPropTypes.string
|
||||
]),
|
||||
|
||||
/** `bottom` is the number of logical pixels to offset the bottom edge of
|
||||
* this component.
|
||||
*
|
||||
* It works similarly to `bottom` in CSS, but in React Native you
|
||||
* must use points or percentages. Ems and other units are not supported.
|
||||
*
|
||||
* See https://developer.mozilla.org/en-US/docs/Web/CSS/bottom
|
||||
* for more details of how `bottom` affects layout.
|
||||
*/
|
||||
bottom: ReactPropTypes.oneOfType([
|
||||
ReactPropTypes.number,
|
||||
ReactPropTypes.string
|
||||
]),
|
||||
|
||||
/** `minWidth` is the minimum width for this component, in logical pixels.
|
||||
*
|
||||
* It works similarly to `min-width` in CSS, but in React Native you
|
||||
* must use points or percentages. Ems and other units are not supported.
|
||||
*
|
||||
* See https://developer.mozilla.org/en-US/docs/Web/CSS/min-width
|
||||
* for more details.
|
||||
*/
|
||||
minWidth: ReactPropTypes.oneOfType([
|
||||
ReactPropTypes.number,
|
||||
ReactPropTypes.string
|
||||
]),
|
||||
|
||||
/** `maxWidth` is the maximum width for this component, in logical pixels.
|
||||
*
|
||||
* It works similarly to `max-width` in CSS, but in React Native you
|
||||
* must use points or percentages. Ems and other units are not supported.
|
||||
*
|
||||
* See https://developer.mozilla.org/en-US/docs/Web/CSS/max-width
|
||||
* for more details.
|
||||
*/
|
||||
maxWidth: ReactPropTypes.oneOfType([
|
||||
ReactPropTypes.number,
|
||||
ReactPropTypes.string
|
||||
]),
|
||||
|
||||
/** `minHeight` is the minimum height for this component, in logical pixels.
|
||||
*
|
||||
* It works similarly to `min-height` in CSS, but in React Native you
|
||||
* must use points or percentages. Ems and other units are not supported.
|
||||
*
|
||||
* See https://developer.mozilla.org/en-US/docs/Web/CSS/min-height
|
||||
* for more details.
|
||||
*/
|
||||
minHeight: ReactPropTypes.oneOfType([
|
||||
ReactPropTypes.number,
|
||||
ReactPropTypes.string
|
||||
]),
|
||||
|
||||
/** `maxHeight` is the maximum height for this component, in logical pixels.
|
||||
*
|
||||
* It works similarly to `max-height` in CSS, but in React Native you
|
||||
* must use points or percentages. Ems and other units are not supported.
|
||||
*
|
||||
* See https://developer.mozilla.org/en-US/docs/Web/CSS/max-height
|
||||
* for more details.
|
||||
*/
|
||||
maxHeight: ReactPropTypes.oneOfType([
|
||||
ReactPropTypes.number,
|
||||
ReactPropTypes.string
|
||||
]),
|
||||
|
||||
/** Setting `margin` has the same effect as setting each of
|
||||
* `marginTop`, `marginLeft`, `marginBottom`, and `marginRight`.
|
||||
* See https://developer.mozilla.org/en-US/docs/Web/CSS/margin
|
||||
* for more details.
|
||||
*/
|
||||
margin: ReactPropTypes.oneOfType([
|
||||
ReactPropTypes.number,
|
||||
ReactPropTypes.string
|
||||
]),
|
||||
|
||||
/** Setting `marginVertical` has the same effect as setting both
|
||||
* `marginTop` and `marginBottom`.
|
||||
*/
|
||||
marginVertical: ReactPropTypes.oneOfType([
|
||||
ReactPropTypes.number,
|
||||
ReactPropTypes.string
|
||||
]),
|
||||
|
||||
/** Setting `marginHorizontal` has the same effect as setting
|
||||
* both `marginLeft` and `marginRight`.
|
||||
*/
|
||||
marginHorizontal: ReactPropTypes.oneOfType([
|
||||
ReactPropTypes.number,
|
||||
ReactPropTypes.string
|
||||
]),
|
||||
|
||||
/** `marginTop` works like `margin-top` in CSS.
|
||||
* See https://developer.mozilla.org/en-US/docs/Web/CSS/margin-top
|
||||
* for more details.
|
||||
*/
|
||||
marginTop: ReactPropTypes.oneOfType([
|
||||
ReactPropTypes.number,
|
||||
ReactPropTypes.string
|
||||
]),
|
||||
|
||||
/** `marginBottom` works like `margin-bottom` in CSS.
|
||||
* See https://developer.mozilla.org/en-US/docs/Web/CSS/margin-bottom
|
||||
* for more details.
|
||||
*/
|
||||
marginBottom: ReactPropTypes.oneOfType([
|
||||
ReactPropTypes.number,
|
||||
ReactPropTypes.string
|
||||
]),
|
||||
|
||||
/** `marginLeft` works like `margin-left` in CSS.
|
||||
* See https://developer.mozilla.org/en-US/docs/Web/CSS/margin-left
|
||||
* for more details.
|
||||
*/
|
||||
marginLeft: ReactPropTypes.oneOfType([
|
||||
ReactPropTypes.number,
|
||||
ReactPropTypes.string
|
||||
]),
|
||||
|
||||
/** `marginRight` works like `margin-right` in CSS.
|
||||
* See https://developer.mozilla.org/en-US/docs/Web/CSS/margin-right
|
||||
* for more details.
|
||||
*/
|
||||
marginRight: ReactPropTypes.oneOfType([
|
||||
ReactPropTypes.number,
|
||||
ReactPropTypes.string
|
||||
]),
|
||||
|
||||
/**
|
||||
* When direction is `ltr`, `marginStart` is equivalent to `marginLeft`.
|
||||
* When direction is `rtl`, `marginStart` is equivalent to `marginRight`.
|
||||
*/
|
||||
marginStart: ReactPropTypes.oneOfType([
|
||||
ReactPropTypes.number,
|
||||
ReactPropTypes.string
|
||||
]),
|
||||
|
||||
/**
|
||||
* When direction is `ltr`, `marginEnd` is equivalent to `marginRight`.
|
||||
* When direction is `rtl`, `marginEnd` is equivalent to `marginLeft`.
|
||||
*/
|
||||
marginEnd: ReactPropTypes.oneOfType([
|
||||
ReactPropTypes.number,
|
||||
ReactPropTypes.string
|
||||
]),
|
||||
|
||||
/** Setting `padding` has the same effect as setting each of
|
||||
* `paddingTop`, `paddingBottom`, `paddingLeft`, and `paddingRight`.
|
||||
* See https://developer.mozilla.org/en-US/docs/Web/CSS/padding
|
||||
* for more details.
|
||||
*/
|
||||
padding: ReactPropTypes.oneOfType([
|
||||
ReactPropTypes.number,
|
||||
ReactPropTypes.string
|
||||
]),
|
||||
|
||||
/** Setting `paddingVertical` is like setting both of
|
||||
* `paddingTop` and `paddingBottom`.
|
||||
*/
|
||||
paddingVertical: ReactPropTypes.oneOfType([
|
||||
ReactPropTypes.number,
|
||||
ReactPropTypes.string
|
||||
]),
|
||||
|
||||
/** Setting `paddingHorizontal` is like setting both of
|
||||
* `paddingLeft` and `paddingRight`.
|
||||
*/
|
||||
paddingHorizontal: ReactPropTypes.oneOfType([
|
||||
ReactPropTypes.number,
|
||||
ReactPropTypes.string
|
||||
]),
|
||||
|
||||
/** `paddingTop` works like `padding-top` in CSS.
|
||||
* See https://developer.mozilla.org/en-US/docs/Web/CSS/padding-top
|
||||
* for more details.
|
||||
*/
|
||||
paddingTop: ReactPropTypes.oneOfType([
|
||||
ReactPropTypes.number,
|
||||
ReactPropTypes.string
|
||||
]),
|
||||
|
||||
/** `paddingBottom` works like `padding-bottom` in CSS.
|
||||
* See https://developer.mozilla.org/en-US/docs/Web/CSS/padding-bottom
|
||||
* for more details.
|
||||
*/
|
||||
paddingBottom: ReactPropTypes.oneOfType([
|
||||
ReactPropTypes.number,
|
||||
ReactPropTypes.string
|
||||
]),
|
||||
|
||||
/** `paddingLeft` works like `padding-left` in CSS.
|
||||
* See https://developer.mozilla.org/en-US/docs/Web/CSS/padding-left
|
||||
* for more details.
|
||||
*/
|
||||
paddingLeft: ReactPropTypes.oneOfType([
|
||||
ReactPropTypes.number,
|
||||
ReactPropTypes.string
|
||||
]),
|
||||
|
||||
/** `paddingRight` works like `padding-right` in CSS.
|
||||
* See https://developer.mozilla.org/en-US/docs/Web/CSS/padding-right
|
||||
* for more details.
|
||||
*/
|
||||
paddingRight: ReactPropTypes.oneOfType([
|
||||
ReactPropTypes.number,
|
||||
ReactPropTypes.string
|
||||
]),
|
||||
|
||||
/**
|
||||
* When direction is `ltr`, `paddingStart` is equivalent to `paddingLeft`.
|
||||
* When direction is `rtl`, `paddingStart` is equivalent to `paddingRight`.
|
||||
*/
|
||||
paddingStart: ReactPropTypes.oneOfType([
|
||||
ReactPropTypes.number,
|
||||
ReactPropTypes.string
|
||||
]),
|
||||
|
||||
/**
|
||||
* When direction is `ltr`, `paddingEnd` is equivalent to `paddingRight`.
|
||||
* When direction is `rtl`, `paddingEnd` is equivalent to `paddingLeft`.
|
||||
*/
|
||||
paddingEnd: ReactPropTypes.oneOfType([
|
||||
ReactPropTypes.number,
|
||||
ReactPropTypes.string
|
||||
]),
|
||||
|
||||
/** `borderWidth` works like `border-width` in CSS.
|
||||
* See https://developer.mozilla.org/en-US/docs/Web/CSS/border-width
|
||||
* for more details.
|
||||
*/
|
||||
borderWidth: ReactPropTypes.number,
|
||||
|
||||
/** `borderTopWidth` works like `border-top-width` in CSS.
|
||||
* See https://developer.mozilla.org/en-US/docs/Web/CSS/border-top-width
|
||||
* for more details.
|
||||
*/
|
||||
borderTopWidth: ReactPropTypes.number,
|
||||
|
||||
/**
|
||||
* When direction is `ltr`, `borderStartWidth` is equivalent to `borderLeftWidth`.
|
||||
* When direction is `rtl`, `borderStartWidth` is equivalent to `borderRightWidth`.
|
||||
*/
|
||||
borderStartWidth: ReactPropTypes.number,
|
||||
|
||||
/**
|
||||
* When direction is `ltr`, `borderEndWidth` is equivalent to `borderRightWidth`.
|
||||
* When direction is `rtl`, `borderEndWidth` is equivalent to `borderLeftWidth`.
|
||||
*/
|
||||
borderEndWidth: ReactPropTypes.number,
|
||||
|
||||
/** `borderRightWidth` works like `border-right-width` in CSS.
|
||||
* See https://developer.mozilla.org/en-US/docs/Web/CSS/border-right-width
|
||||
* for more details.
|
||||
*/
|
||||
borderRightWidth: ReactPropTypes.number,
|
||||
|
||||
/** `borderBottomWidth` works like `border-bottom-width` in CSS.
|
||||
* See https://developer.mozilla.org/en-US/docs/Web/CSS/border-bottom-width
|
||||
* for more details.
|
||||
*/
|
||||
borderBottomWidth: ReactPropTypes.number,
|
||||
|
||||
/** `borderLeftWidth` works like `border-left-width` in CSS.
|
||||
* See https://developer.mozilla.org/en-US/docs/Web/CSS/border-left-width
|
||||
* for more details.
|
||||
*/
|
||||
borderLeftWidth: ReactPropTypes.number,
|
||||
|
||||
/** `position` in React Native is similar to regular CSS, but
|
||||
* everything is set to `relative` by default, so `absolute`
|
||||
* positioning is always just relative to the parent.
|
||||
*
|
||||
* If you want to position a child using specific numbers of logical
|
||||
* pixels relative to its parent, set the child to have `absolute`
|
||||
* position.
|
||||
*
|
||||
* If you want to position a child relative to something
|
||||
* that is not its parent, just don't use styles for that. Use the
|
||||
* component tree.
|
||||
*
|
||||
* See https://github.com/facebook/yoga
|
||||
* for more details on how `position` differs between React Native
|
||||
* and CSS.
|
||||
*/
|
||||
position: ReactPropTypes.oneOf(['absolute', 'relative']),
|
||||
|
||||
/** `flexDirection` controls which directions children of a container go.
|
||||
* `row` goes left to right, `column` goes top to bottom, and you may
|
||||
* be able to guess what the other two do. It works like `flex-direction`
|
||||
* in CSS, except the default is `column`.
|
||||
* See https://developer.mozilla.org/en-US/docs/Web/CSS/flex-direction
|
||||
* for more details.
|
||||
*/
|
||||
flexDirection: ReactPropTypes.oneOf([
|
||||
'row',
|
||||
'row-reverse',
|
||||
'column',
|
||||
'column-reverse'
|
||||
]),
|
||||
|
||||
/** `flexWrap` controls whether children can wrap around after they
|
||||
* hit the end of a flex container.
|
||||
* It works like `flex-wrap` in CSS (default: nowrap).
|
||||
* See https://developer.mozilla.org/en-US/docs/Web/CSS/flex-wrap
|
||||
* for more details.
|
||||
*/
|
||||
flexWrap: ReactPropTypes.oneOf(['wrap', 'nowrap', 'wrap-reverse']),
|
||||
|
||||
/** `justifyContent` aligns children in the main direction.
|
||||
* For example, if children are flowing vertically, `justifyContent`
|
||||
* controls how they align vertically.
|
||||
* It works like `justify-content` in CSS (default: flex-start).
|
||||
* See https://developer.mozilla.org/en-US/docs/Web/CSS/justify-content
|
||||
* for more details.
|
||||
*/
|
||||
justifyContent: ReactPropTypes.oneOf([
|
||||
'flex-start',
|
||||
'flex-end',
|
||||
'center',
|
||||
'space-between',
|
||||
'space-around',
|
||||
'space-evenly'
|
||||
]),
|
||||
|
||||
/** `alignItems` aligns children in the cross direction.
|
||||
* For example, if children are flowing vertically, `alignItems`
|
||||
* controls how they align horizontally.
|
||||
* It works like `align-items` in CSS (default: stretch).
|
||||
* See https://developer.mozilla.org/en-US/docs/Web/CSS/align-items
|
||||
* for more details.
|
||||
*/
|
||||
alignItems: ReactPropTypes.oneOf([
|
||||
'flex-start',
|
||||
'flex-end',
|
||||
'center',
|
||||
'stretch',
|
||||
'baseline'
|
||||
]),
|
||||
|
||||
/** `alignSelf` controls how a child aligns in the cross direction,
|
||||
* overriding the `alignItems` of the parent. It works like `align-self`
|
||||
* in CSS (default: auto).
|
||||
* See https://developer.mozilla.org/en-US/docs/Web/CSS/align-self
|
||||
* for more details.
|
||||
*/
|
||||
alignSelf: ReactPropTypes.oneOf([
|
||||
'auto',
|
||||
'flex-start',
|
||||
'flex-end',
|
||||
'center',
|
||||
'stretch',
|
||||
'baseline'
|
||||
]),
|
||||
|
||||
/** `alignContent` controls how rows align in the cross direction,
|
||||
* overriding the `alignContent` of the parent.
|
||||
* See https://developer.mozilla.org/en-US/docs/Web/CSS/align-content
|
||||
* for more details.
|
||||
*/
|
||||
alignContent: ReactPropTypes.oneOf([
|
||||
'flex-start',
|
||||
'flex-end',
|
||||
'center',
|
||||
'stretch',
|
||||
'space-between',
|
||||
'space-around'
|
||||
]),
|
||||
|
||||
/** `overflow` controls how children are measured and displayed.
|
||||
* `overflow: hidden` causes views to be clipped while `overflow: scroll`
|
||||
* causes views to be measured independently of their parents main axis.
|
||||
* It works like `overflow` in CSS (default: visible).
|
||||
* See https://developer.mozilla.org/en/docs/Web/CSS/overflow
|
||||
* for more details.
|
||||
* `overflow: visible` only works on iOS. On Android, all views will clip
|
||||
* their children.
|
||||
*/
|
||||
overflow: ReactPropTypes.oneOf(['visible', 'hidden', 'scroll']),
|
||||
|
||||
/** In React Native `flex` does not work the same way that it does in CSS.
|
||||
* `flex` is a number rather than a string, and it works
|
||||
* according to the `Yoga` library
|
||||
* at https://github.com/facebook/yoga
|
||||
*
|
||||
* When `flex` is a positive number, it makes the component flexible
|
||||
* and it will be sized proportional to its flex value. So a
|
||||
* component with `flex` set to 2 will take twice the space as a
|
||||
* component with `flex` set to 1.
|
||||
*
|
||||
* When `flex` is 0, the component is sized according to `width`
|
||||
* and `height` and it is inflexible.
|
||||
*
|
||||
* When `flex` is -1, the component is normally sized according
|
||||
* `width` and `height`. However, if there's not enough space,
|
||||
* the component will shrink to its `minWidth` and `minHeight`.
|
||||
*
|
||||
* flexGrow, flexShrink, and flexBasis work the same as in CSS.
|
||||
*/
|
||||
flex: ReactPropTypes.number,
|
||||
flexGrow: ReactPropTypes.number,
|
||||
flexShrink: ReactPropTypes.number,
|
||||
flexBasis: ReactPropTypes.oneOfType([
|
||||
ReactPropTypes.number,
|
||||
ReactPropTypes.string
|
||||
]),
|
||||
|
||||
/**
|
||||
* Aspect ratio control the size of the undefined dimension of a node. Aspect ratio is a
|
||||
* non-standard property only available in react native and not CSS.
|
||||
*
|
||||
* - On a node with a set width/height aspect ratio control the size of the unset dimension
|
||||
* - On a node with a set flex basis aspect ratio controls the size of the node in the cross axis
|
||||
* if unset
|
||||
* - On a node with a measure function aspect ratio works as though the measure function measures
|
||||
* the flex basis
|
||||
* - On a node with flex grow/shrink aspect ratio controls the size of the node in the cross axis
|
||||
* if unset
|
||||
* - Aspect ratio takes min/max dimensions into account
|
||||
*/
|
||||
aspectRatio: ReactPropTypes.number,
|
||||
|
||||
/** `zIndex` controls which components display on top of others.
|
||||
* Normally, you don't use `zIndex`. Components render according to
|
||||
* their order in the document tree, so later components draw over
|
||||
* earlier ones. `zIndex` may be useful if you have animations or custom
|
||||
* modal interfaces where you don't want this behavior.
|
||||
*
|
||||
* It works like the CSS `z-index` property - components with a larger
|
||||
* `zIndex` will render on top. Think of the z-direction like it's
|
||||
* pointing from the phone into your eyeball.
|
||||
* See https://developer.mozilla.org/en-US/docs/Web/CSS/z-index for
|
||||
* more details.
|
||||
*/
|
||||
zIndex: ReactPropTypes.number,
|
||||
|
||||
/** `direction` specifies the directional flow of the user interface.
|
||||
* The default is `inherit`, except for root node which will have
|
||||
* value based on the current locale.
|
||||
* See https://facebook.github.io/yoga/docs/rtl/
|
||||
* for more details.
|
||||
* @platform ios
|
||||
*/
|
||||
direction: ReactPropTypes.oneOf(['inherit', 'ltr', 'rtl'])
|
||||
}
|
||||
|
||||
module.exports = LayoutPropTypes
|
|
@ -0,0 +1,50 @@
|
|||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
*
|
||||
* @format
|
||||
*/
|
||||
'use strict'
|
||||
|
||||
const ColorPropType = require('./ColorPropType')
|
||||
const ReactPropTypes = require('prop-types')
|
||||
|
||||
/**
|
||||
* These props can be used to dynamically generate shadows on views, images, text, etc.
|
||||
*
|
||||
* Because they are dynamically generated, they may cause performance regressions. Static
|
||||
* shadow image asset may be a better way to go for optimal performance.
|
||||
*
|
||||
* These properties are iOS only - for similar functionality on Android, use the [`elevation`
|
||||
* property](docs/viewstyleproptypes.html#elevation).
|
||||
*/
|
||||
const ShadowPropTypesIOS = {
|
||||
/**
|
||||
* Sets the drop shadow color
|
||||
* @platform ios
|
||||
*/
|
||||
shadowColor: ColorPropType,
|
||||
/**
|
||||
* Sets the drop shadow offset
|
||||
* @platform ios
|
||||
*/
|
||||
shadowOffset: ReactPropTypes.shape({
|
||||
width: ReactPropTypes.number,
|
||||
height: ReactPropTypes.number
|
||||
}),
|
||||
/**
|
||||
* Sets the drop shadow opacity (multiplied by the color's alpha component)
|
||||
* @platform ios
|
||||
*/
|
||||
shadowOpacity: ReactPropTypes.number,
|
||||
/**
|
||||
* Sets the drop shadow blur radius
|
||||
* @platform ios
|
||||
*/
|
||||
shadowRadius: ReactPropTypes.number
|
||||
}
|
||||
|
||||
module.exports = ShadowPropTypesIOS
|
|
@ -0,0 +1,82 @@
|
|||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @format
|
||||
*
|
||||
*/
|
||||
|
||||
'use strict'
|
||||
|
||||
const ImageStylePropTypes = require('./ImageStylePropTypes')
|
||||
const TextStylePropTypes = require('./TextStylePropTypes')
|
||||
const ViewStylePropTypes = require('./ViewStylePropTypes')
|
||||
|
||||
const invariant = require('fbjs/lib/invariant')
|
||||
|
||||
// Hardcoded because this is a legit case but we don't want to load it from
|
||||
// a private API. We might likely want to unify style sheet creation with how it
|
||||
// is done in the DOM so this might move into React. I know what I'm doing so
|
||||
// plz don't fire me.
|
||||
const ReactPropTypesSecret = 'SECRET_DO_NOT_PASS_THIS_OR_YOU_WILL_BE_FIRED'
|
||||
|
||||
class StyleSheetValidation {
|
||||
static validateStyleProp (prop, style, caller) {
|
||||
if (allStylePropTypes[prop] === undefined) {
|
||||
const message1 = '"' + prop + '" is not a valid style property.'
|
||||
const message2 =
|
||||
'\nValid style props: ' +
|
||||
JSON.stringify(Object.keys(allStylePropTypes).sort(), null, ' ')
|
||||
styleError(message1, style, caller, message2)
|
||||
}
|
||||
const error = allStylePropTypes[prop](
|
||||
style,
|
||||
prop,
|
||||
caller,
|
||||
'prop',
|
||||
null,
|
||||
ReactPropTypesSecret
|
||||
)
|
||||
if (error) {
|
||||
styleError(error.message, style, caller)
|
||||
}
|
||||
}
|
||||
|
||||
static validateStyle (name, styles) {
|
||||
for (const prop in styles[name]) {
|
||||
StyleSheetValidation.validateStyleProp(
|
||||
prop,
|
||||
styles[name],
|
||||
'StyleSheet ' + name
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
static addValidStylePropTypes (stylePropTypes) {
|
||||
for (const key in stylePropTypes) {
|
||||
allStylePropTypes[key] = stylePropTypes[key]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const styleError = function (message1, style, caller, message2) {
|
||||
invariant(
|
||||
false,
|
||||
message1 +
|
||||
'\n' +
|
||||
(caller || '<<unknown>>') +
|
||||
': ' +
|
||||
JSON.stringify(style, null, ' ') +
|
||||
(message2 || '')
|
||||
)
|
||||
}
|
||||
|
||||
const allStylePropTypes = {}
|
||||
|
||||
StyleSheetValidation.addValidStylePropTypes(ImageStylePropTypes)
|
||||
StyleSheetValidation.addValidStylePropTypes(TextStylePropTypes)
|
||||
StyleSheetValidation.addValidStylePropTypes(ViewStylePropTypes)
|
||||
|
||||
module.exports = StyleSheetValidation
|
|
@ -0,0 +1,123 @@
|
|||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @format
|
||||
*
|
||||
*/
|
||||
|
||||
'use strict'
|
||||
|
||||
const ColorPropType = require('./ColorPropType')
|
||||
const ReactPropTypes = require('prop-types')
|
||||
const ViewStylePropTypes = require('./ViewStylePropTypes')
|
||||
|
||||
const TextStylePropTypes = {
|
||||
...ViewStylePropTypes,
|
||||
|
||||
color: ColorPropType,
|
||||
fontFamily: ReactPropTypes.string,
|
||||
fontSize: ReactPropTypes.number,
|
||||
fontStyle: ReactPropTypes.oneOf(['normal', 'italic']),
|
||||
/**
|
||||
* Specifies font weight. The values 'normal' and 'bold' are supported for
|
||||
* most fonts. Not all fonts have a variant for each of the numeric values,
|
||||
* in that case the closest one is chosen.
|
||||
*/
|
||||
fontWeight: ReactPropTypes.oneOf([
|
||||
'normal' /* default */,
|
||||
'bold',
|
||||
'100',
|
||||
'200',
|
||||
'300',
|
||||
'400',
|
||||
'500',
|
||||
'600',
|
||||
'700',
|
||||
'800',
|
||||
'900'
|
||||
]),
|
||||
/**
|
||||
* @platform ios
|
||||
*/
|
||||
fontVariant: ReactPropTypes.arrayOf(
|
||||
ReactPropTypes.oneOf([
|
||||
'small-caps',
|
||||
'oldstyle-nums',
|
||||
'lining-nums',
|
||||
'tabular-nums',
|
||||
'proportional-nums'
|
||||
])
|
||||
),
|
||||
textShadowOffset: ReactPropTypes.shape({
|
||||
width: ReactPropTypes.number,
|
||||
height: ReactPropTypes.number
|
||||
}),
|
||||
textShadowRadius: ReactPropTypes.number,
|
||||
textShadowColor: ColorPropType,
|
||||
/**
|
||||
* @platform ios
|
||||
*/
|
||||
letterSpacing: ReactPropTypes.number,
|
||||
lineHeight: ReactPropTypes.number,
|
||||
/**
|
||||
* Specifies text alignment. The value 'justify' is only supported on iOS and
|
||||
* fallbacks to `left` on Android.
|
||||
*/
|
||||
textAlign: ReactPropTypes.oneOf([
|
||||
'auto' /* default */,
|
||||
'left',
|
||||
'right',
|
||||
'center',
|
||||
'justify'
|
||||
]),
|
||||
/**
|
||||
* @platform android
|
||||
*/
|
||||
textAlignVertical: ReactPropTypes.oneOf([
|
||||
'auto' /* default */,
|
||||
'top',
|
||||
'bottom',
|
||||
'center'
|
||||
]),
|
||||
/**
|
||||
* Set to `false` to remove extra font padding intended to make space for certain ascenders / descenders.
|
||||
* With some fonts, this padding can make text look slightly misaligned when centered vertically.
|
||||
* For best results also set `textAlignVertical` to `center`. Default is true.
|
||||
* @platform android
|
||||
*/
|
||||
includeFontPadding: ReactPropTypes.bool,
|
||||
textDecorationLine: ReactPropTypes.oneOf([
|
||||
'none' /* default */,
|
||||
'underline',
|
||||
'line-through',
|
||||
'underline line-through'
|
||||
]),
|
||||
/**
|
||||
* @platform ios
|
||||
*/
|
||||
textDecorationStyle: ReactPropTypes.oneOf([
|
||||
'solid' /* default */,
|
||||
'double',
|
||||
'dotted',
|
||||
'dashed'
|
||||
]),
|
||||
/**
|
||||
* @platform ios
|
||||
*/
|
||||
textDecorationColor: ColorPropType,
|
||||
textTransform: ReactPropTypes.oneOf([
|
||||
'none' /* default */,
|
||||
'capitalize',
|
||||
'uppercase',
|
||||
'lowercase'
|
||||
]),
|
||||
/**
|
||||
* @platform ios
|
||||
*/
|
||||
writingDirection: ReactPropTypes.oneOf(['auto' /* default */, 'ltr', 'rtl'])
|
||||
}
|
||||
|
||||
module.exports = TextStylePropTypes
|
|
@ -0,0 +1,109 @@
|
|||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @format
|
||||
*
|
||||
*/
|
||||
|
||||
'use strict'
|
||||
|
||||
const ReactPropTypes = require('prop-types')
|
||||
|
||||
const deprecatedPropType = require('./deprecatedPropType')
|
||||
|
||||
const TransformMatrixPropType = function (
|
||||
props,
|
||||
propName,
|
||||
componentName
|
||||
) {
|
||||
if (props[propName]) {
|
||||
return new Error(
|
||||
'The transformMatrix style property is deprecated. ' +
|
||||
'Use `transform: [{ matrix: ... }]` instead.'
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const DecomposedMatrixPropType = function (
|
||||
props,
|
||||
propName,
|
||||
componentName
|
||||
) {
|
||||
if (props[propName]) {
|
||||
return new Error(
|
||||
'The decomposedMatrix style property is deprecated. ' +
|
||||
'Use `transform: [...]` instead.'
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const TransformPropTypes = {
|
||||
/**
|
||||
* `transform` accepts an array of transformation objects. Each object specifies
|
||||
* the property that will be transformed as the key, and the value to use in the
|
||||
* transformation. Objects should not be combined. Use a single key/value pair
|
||||
* per object.
|
||||
*
|
||||
* The rotate transformations require a string so that the transform may be
|
||||
* expressed in degrees (deg) or radians (rad). For example:
|
||||
*
|
||||
* `transform([{ rotateX: '45deg' }, { rotateZ: '0.785398rad' }])`
|
||||
*
|
||||
* The skew transformations require a string so that the transform may be
|
||||
* expressed in degrees (deg). For example:
|
||||
*
|
||||
* `transform([{ skewX: '45deg' }])`
|
||||
*/
|
||||
transform: ReactPropTypes.arrayOf(
|
||||
ReactPropTypes.oneOfType([
|
||||
ReactPropTypes.shape({perspective: ReactPropTypes.number}),
|
||||
ReactPropTypes.shape({rotate: ReactPropTypes.string}),
|
||||
ReactPropTypes.shape({rotateX: ReactPropTypes.string}),
|
||||
ReactPropTypes.shape({rotateY: ReactPropTypes.string}),
|
||||
ReactPropTypes.shape({rotateZ: ReactPropTypes.string}),
|
||||
ReactPropTypes.shape({scale: ReactPropTypes.number}),
|
||||
ReactPropTypes.shape({scaleX: ReactPropTypes.number}),
|
||||
ReactPropTypes.shape({scaleY: ReactPropTypes.number}),
|
||||
ReactPropTypes.shape({translateX: ReactPropTypes.number}),
|
||||
ReactPropTypes.shape({translateY: ReactPropTypes.number}),
|
||||
ReactPropTypes.shape({skewX: ReactPropTypes.string}),
|
||||
ReactPropTypes.shape({skewY: ReactPropTypes.string})
|
||||
])
|
||||
),
|
||||
|
||||
/**
|
||||
* Deprecated. Use the transform prop instead.
|
||||
*/
|
||||
transformMatrix: TransformMatrixPropType,
|
||||
/**
|
||||
* Deprecated. Use the transform prop instead.
|
||||
*/
|
||||
decomposedMatrix: DecomposedMatrixPropType,
|
||||
|
||||
/* Deprecated transform props used on Android only */
|
||||
scaleX: deprecatedPropType(
|
||||
ReactPropTypes.number,
|
||||
'Use the transform prop instead.'
|
||||
),
|
||||
scaleY: deprecatedPropType(
|
||||
ReactPropTypes.number,
|
||||
'Use the transform prop instead.'
|
||||
),
|
||||
rotation: deprecatedPropType(
|
||||
ReactPropTypes.number,
|
||||
'Use the transform prop instead.'
|
||||
),
|
||||
translateX: deprecatedPropType(
|
||||
ReactPropTypes.number,
|
||||
'Use the transform prop instead.'
|
||||
),
|
||||
translateY: deprecatedPropType(
|
||||
ReactPropTypes.number,
|
||||
'Use the transform prop instead.'
|
||||
)
|
||||
}
|
||||
|
||||
module.exports = TransformPropTypes
|
|
@ -0,0 +1,61 @@
|
|||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @format
|
||||
*
|
||||
*/
|
||||
|
||||
'use strict'
|
||||
|
||||
const ColorPropType = require('./ColorPropType')
|
||||
const LayoutPropTypes = require('./LayoutPropTypes')
|
||||
const ReactPropTypes = require('prop-types')
|
||||
const ShadowPropTypesIOS = require('./ShadowPropTypesIOS')
|
||||
const TransformPropTypes = require('./TransformPropTypes')
|
||||
|
||||
/**
|
||||
* Warning: Some of these properties may not be supported in all releases.
|
||||
*/
|
||||
const ViewStylePropTypes = {
|
||||
...LayoutPropTypes,
|
||||
...ShadowPropTypesIOS,
|
||||
...TransformPropTypes,
|
||||
backfaceVisibility: ReactPropTypes.oneOf(['visible', 'hidden']),
|
||||
backgroundColor: ColorPropType,
|
||||
borderColor: ColorPropType,
|
||||
borderTopColor: ColorPropType,
|
||||
borderRightColor: ColorPropType,
|
||||
borderBottomColor: ColorPropType,
|
||||
borderLeftColor: ColorPropType,
|
||||
borderStartColor: ColorPropType,
|
||||
borderEndColor: ColorPropType,
|
||||
borderRadius: ReactPropTypes.number,
|
||||
borderTopLeftRadius: ReactPropTypes.number,
|
||||
borderTopRightRadius: ReactPropTypes.number,
|
||||
borderTopStartRadius: ReactPropTypes.number,
|
||||
borderTopEndRadius: ReactPropTypes.number,
|
||||
borderBottomLeftRadius: ReactPropTypes.number,
|
||||
borderBottomRightRadius: ReactPropTypes.number,
|
||||
borderBottomStartRadius: ReactPropTypes.number,
|
||||
borderBottomEndRadius: ReactPropTypes.number,
|
||||
borderStyle: ReactPropTypes.oneOf(['solid', 'dotted', 'dashed']),
|
||||
borderWidth: ReactPropTypes.number,
|
||||
borderTopWidth: ReactPropTypes.number,
|
||||
borderRightWidth: ReactPropTypes.number,
|
||||
borderBottomWidth: ReactPropTypes.number,
|
||||
borderLeftWidth: ReactPropTypes.number,
|
||||
opacity: ReactPropTypes.number,
|
||||
/**
|
||||
* (Android-only) Sets the elevation of a view, using Android's underlying
|
||||
* [elevation API](https://developer.android.com/training/material/shadows-clipping.html#Elevation).
|
||||
* This adds a drop shadow to the item and affects z-order for overlapping views.
|
||||
* Only supported on Android 5.0+, has no effect on earlier versions.
|
||||
* @platform android
|
||||
*/
|
||||
elevation: ReactPropTypes.number
|
||||
}
|
||||
|
||||
module.exports = ViewStylePropTypes
|
|
@ -0,0 +1,35 @@
|
|||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @format
|
||||
* strict-local
|
||||
*/
|
||||
|
||||
'use strict'
|
||||
|
||||
// const UIManager = require('UIManager')
|
||||
|
||||
/**
|
||||
* Adds a deprecation warning when the prop is used.
|
||||
*/
|
||||
function deprecatedPropType (
|
||||
propType,
|
||||
explanation
|
||||
) {
|
||||
return function validate (props, propName, componentName, ...rest) {
|
||||
// Don't warn for native components.
|
||||
// if (!UIManager[componentName] && props[propName] !== undefined) {
|
||||
if (props[propName] !== undefined) {
|
||||
console.warn(
|
||||
`\`${propName}\` supplied to \`${componentName}\` has been deprecated. ${explanation}`
|
||||
)
|
||||
}
|
||||
|
||||
return propType(props, propName, componentName, ...rest)
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = deprecatedPropType
|
|
@ -0,0 +1,3 @@
|
|||
const StyleSheetValidation = require('./StyleSheetValidation')
|
||||
|
||||
module.exports = {StyleSheetValidation}
|
|
@ -0,0 +1,372 @@
|
|||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @format
|
||||
*
|
||||
*/
|
||||
|
||||
/* eslint no-bitwise: 0 */
|
||||
'use strict'
|
||||
|
||||
function normalizeColor (color) {
|
||||
const matchers = getMatchers()
|
||||
let match
|
||||
|
||||
if (typeof color === 'number') {
|
||||
if (color >>> 0 === color && color >= 0 && color <= 0xffffffff) {
|
||||
return color
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
// Ordered based on occurrences on Facebook codebase
|
||||
if ((match = matchers.hex6.exec(color))) {
|
||||
return parseInt(match[1] + 'ff', 16) >>> 0
|
||||
}
|
||||
|
||||
if (names.hasOwnProperty(color)) {
|
||||
return names[color]
|
||||
}
|
||||
|
||||
if ((match = matchers.rgb.exec(color))) {
|
||||
return (
|
||||
// b
|
||||
((parse255(match[1]) << 24) | // r
|
||||
(parse255(match[2]) << 16) | // g
|
||||
(parse255(match[3]) << 8) |
|
||||
0x000000ff) >>> // a
|
||||
0
|
||||
)
|
||||
}
|
||||
|
||||
if ((match = matchers.rgba.exec(color))) {
|
||||
return (
|
||||
// b
|
||||
((parse255(match[1]) << 24) | // r
|
||||
(parse255(match[2]) << 16) | // g
|
||||
(parse255(match[3]) << 8) |
|
||||
parse1(match[4])) >>> // a
|
||||
0
|
||||
)
|
||||
}
|
||||
|
||||
if ((match = matchers.hex3.exec(color))) {
|
||||
return (
|
||||
parseInt(
|
||||
match[1] +
|
||||
match[1] + // r
|
||||
match[2] +
|
||||
match[2] + // g
|
||||
match[3] +
|
||||
match[3] + // b
|
||||
'ff', // a
|
||||
16
|
||||
) >>> 0
|
||||
)
|
||||
}
|
||||
|
||||
// https://drafts.csswg.org/css-color-4/#hex-notation
|
||||
if ((match = matchers.hex8.exec(color))) {
|
||||
return parseInt(match[1], 16) >>> 0
|
||||
}
|
||||
|
||||
if ((match = matchers.hex4.exec(color))) {
|
||||
return (
|
||||
parseInt(
|
||||
match[1] +
|
||||
match[1] + // r
|
||||
match[2] +
|
||||
match[2] + // g
|
||||
match[3] +
|
||||
match[3] + // b
|
||||
match[4] +
|
||||
match[4], // a
|
||||
16
|
||||
) >>> 0
|
||||
)
|
||||
}
|
||||
|
||||
if ((match = matchers.hsl.exec(color))) {
|
||||
return (
|
||||
(hslToRgb(
|
||||
parse360(match[1]), // h
|
||||
parsePercentage(match[2]), // s
|
||||
parsePercentage(match[3]) // l
|
||||
) |
|
||||
0x000000ff) >>> // a
|
||||
0
|
||||
)
|
||||
}
|
||||
|
||||
if ((match = matchers.hsla.exec(color))) {
|
||||
return (
|
||||
(hslToRgb(
|
||||
parse360(match[1]), // h
|
||||
parsePercentage(match[2]), // s
|
||||
parsePercentage(match[3]) // l
|
||||
) |
|
||||
parse1(match[4])) >>> // a
|
||||
0
|
||||
)
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
function hue2rgb (p, q, t) {
|
||||
if (t < 0) {
|
||||
t += 1
|
||||
}
|
||||
if (t > 1) {
|
||||
t -= 1
|
||||
}
|
||||
if (t < 1 / 6) {
|
||||
return p + (q - p) * 6 * t
|
||||
}
|
||||
if (t < 1 / 2) {
|
||||
return q
|
||||
}
|
||||
if (t < 2 / 3) {
|
||||
return p + (q - p) * (2 / 3 - t) * 6
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
||||
function hslToRgb (h, s, l) {
|
||||
const q = l < 0.5 ? l * (1 + s) : l + s - l * s
|
||||
const p = 2 * l - q
|
||||
const r = hue2rgb(p, q, h + 1 / 3)
|
||||
const g = hue2rgb(p, q, h)
|
||||
const b = hue2rgb(p, q, h - 1 / 3)
|
||||
|
||||
return (
|
||||
(Math.round(r * 255) << 24) |
|
||||
(Math.round(g * 255) << 16) |
|
||||
(Math.round(b * 255) << 8)
|
||||
)
|
||||
}
|
||||
|
||||
// var INTEGER = '[-+]?\\d+';
|
||||
const NUMBER = '[-+]?\\d*\\.?\\d+'
|
||||
const PERCENTAGE = NUMBER + '%'
|
||||
|
||||
function call (...args) {
|
||||
return '\\(\\s*(' + args.join(')\\s*,\\s*(') + ')\\s*\\)'
|
||||
}
|
||||
|
||||
let cachedMatchers
|
||||
|
||||
function getMatchers () {
|
||||
if (cachedMatchers === undefined) {
|
||||
cachedMatchers = {
|
||||
rgb: new RegExp('rgb' + call(NUMBER, NUMBER, NUMBER)),
|
||||
rgba: new RegExp('rgba' + call(NUMBER, NUMBER, NUMBER, NUMBER)),
|
||||
hsl: new RegExp('hsl' + call(NUMBER, PERCENTAGE, PERCENTAGE)),
|
||||
hsla: new RegExp('hsla' + call(NUMBER, PERCENTAGE, PERCENTAGE, NUMBER)),
|
||||
hex3: /^#([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})$/,
|
||||
hex4: /^#([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})$/,
|
||||
hex6: /^#([0-9a-fA-F]{6})$/,
|
||||
hex8: /^#([0-9a-fA-F]{8})$/
|
||||
}
|
||||
}
|
||||
return cachedMatchers
|
||||
}
|
||||
|
||||
function parse255 (str) {
|
||||
const int = parseInt(str, 10)
|
||||
if (int < 0) {
|
||||
return 0
|
||||
}
|
||||
if (int > 255) {
|
||||
return 255
|
||||
}
|
||||
return int
|
||||
}
|
||||
|
||||
function parse360 (str) {
|
||||
const int = parseFloat(str)
|
||||
return (((int % 360) + 360) % 360) / 360
|
||||
}
|
||||
|
||||
function parse1 (str) {
|
||||
const num = parseFloat(str)
|
||||
if (num < 0) {
|
||||
return 0
|
||||
}
|
||||
if (num > 1) {
|
||||
return 255
|
||||
}
|
||||
return Math.round(num * 255)
|
||||
}
|
||||
|
||||
function parsePercentage (str) {
|
||||
// parseFloat conveniently ignores the final %
|
||||
const int = parseFloat(str)
|
||||
if (int < 0) {
|
||||
return 0
|
||||
}
|
||||
if (int > 100) {
|
||||
return 1
|
||||
}
|
||||
return int / 100
|
||||
}
|
||||
|
||||
const names = {
|
||||
transparent: 0x00000000,
|
||||
|
||||
// http://www.w3.org/TR/css3-color/#svg-color
|
||||
aliceblue: 0xf0f8ffff,
|
||||
antiquewhite: 0xfaebd7ff,
|
||||
aqua: 0x00ffffff,
|
||||
aquamarine: 0x7fffd4ff,
|
||||
azure: 0xf0ffffff,
|
||||
beige: 0xf5f5dcff,
|
||||
bisque: 0xffe4c4ff,
|
||||
black: 0x000000ff,
|
||||
blanchedalmond: 0xffebcdff,
|
||||
blue: 0x0000ffff,
|
||||
blueviolet: 0x8a2be2ff,
|
||||
brown: 0xa52a2aff,
|
||||
burlywood: 0xdeb887ff,
|
||||
burntsienna: 0xea7e5dff,
|
||||
cadetblue: 0x5f9ea0ff,
|
||||
chartreuse: 0x7fff00ff,
|
||||
chocolate: 0xd2691eff,
|
||||
coral: 0xff7f50ff,
|
||||
cornflowerblue: 0x6495edff,
|
||||
cornsilk: 0xfff8dcff,
|
||||
crimson: 0xdc143cff,
|
||||
cyan: 0x00ffffff,
|
||||
darkblue: 0x00008bff,
|
||||
darkcyan: 0x008b8bff,
|
||||
darkgoldenrod: 0xb8860bff,
|
||||
darkgray: 0xa9a9a9ff,
|
||||
darkgreen: 0x006400ff,
|
||||
darkgrey: 0xa9a9a9ff,
|
||||
darkkhaki: 0xbdb76bff,
|
||||
darkmagenta: 0x8b008bff,
|
||||
darkolivegreen: 0x556b2fff,
|
||||
darkorange: 0xff8c00ff,
|
||||
darkorchid: 0x9932ccff,
|
||||
darkred: 0x8b0000ff,
|
||||
darksalmon: 0xe9967aff,
|
||||
darkseagreen: 0x8fbc8fff,
|
||||
darkslateblue: 0x483d8bff,
|
||||
darkslategray: 0x2f4f4fff,
|
||||
darkslategrey: 0x2f4f4fff,
|
||||
darkturquoise: 0x00ced1ff,
|
||||
darkviolet: 0x9400d3ff,
|
||||
deeppink: 0xff1493ff,
|
||||
deepskyblue: 0x00bfffff,
|
||||
dimgray: 0x696969ff,
|
||||
dimgrey: 0x696969ff,
|
||||
dodgerblue: 0x1e90ffff,
|
||||
firebrick: 0xb22222ff,
|
||||
floralwhite: 0xfffaf0ff,
|
||||
forestgreen: 0x228b22ff,
|
||||
fuchsia: 0xff00ffff,
|
||||
gainsboro: 0xdcdcdcff,
|
||||
ghostwhite: 0xf8f8ffff,
|
||||
gold: 0xffd700ff,
|
||||
goldenrod: 0xdaa520ff,
|
||||
gray: 0x808080ff,
|
||||
green: 0x008000ff,
|
||||
greenyellow: 0xadff2fff,
|
||||
grey: 0x808080ff,
|
||||
honeydew: 0xf0fff0ff,
|
||||
hotpink: 0xff69b4ff,
|
||||
indianred: 0xcd5c5cff,
|
||||
indigo: 0x4b0082ff,
|
||||
ivory: 0xfffff0ff,
|
||||
khaki: 0xf0e68cff,
|
||||
lavender: 0xe6e6faff,
|
||||
lavenderblush: 0xfff0f5ff,
|
||||
lawngreen: 0x7cfc00ff,
|
||||
lemonchiffon: 0xfffacdff,
|
||||
lightblue: 0xadd8e6ff,
|
||||
lightcoral: 0xf08080ff,
|
||||
lightcyan: 0xe0ffffff,
|
||||
lightgoldenrodyellow: 0xfafad2ff,
|
||||
lightgray: 0xd3d3d3ff,
|
||||
lightgreen: 0x90ee90ff,
|
||||
lightgrey: 0xd3d3d3ff,
|
||||
lightpink: 0xffb6c1ff,
|
||||
lightsalmon: 0xffa07aff,
|
||||
lightseagreen: 0x20b2aaff,
|
||||
lightskyblue: 0x87cefaff,
|
||||
lightslategray: 0x778899ff,
|
||||
lightslategrey: 0x778899ff,
|
||||
lightsteelblue: 0xb0c4deff,
|
||||
lightyellow: 0xffffe0ff,
|
||||
lime: 0x00ff00ff,
|
||||
limegreen: 0x32cd32ff,
|
||||
linen: 0xfaf0e6ff,
|
||||
magenta: 0xff00ffff,
|
||||
maroon: 0x800000ff,
|
||||
mediumaquamarine: 0x66cdaaff,
|
||||
mediumblue: 0x0000cdff,
|
||||
mediumorchid: 0xba55d3ff,
|
||||
mediumpurple: 0x9370dbff,
|
||||
mediumseagreen: 0x3cb371ff,
|
||||
mediumslateblue: 0x7b68eeff,
|
||||
mediumspringgreen: 0x00fa9aff,
|
||||
mediumturquoise: 0x48d1ccff,
|
||||
mediumvioletred: 0xc71585ff,
|
||||
midnightblue: 0x191970ff,
|
||||
mintcream: 0xf5fffaff,
|
||||
mistyrose: 0xffe4e1ff,
|
||||
moccasin: 0xffe4b5ff,
|
||||
navajowhite: 0xffdeadff,
|
||||
navy: 0x000080ff,
|
||||
oldlace: 0xfdf5e6ff,
|
||||
olive: 0x808000ff,
|
||||
olivedrab: 0x6b8e23ff,
|
||||
orange: 0xffa500ff,
|
||||
orangered: 0xff4500ff,
|
||||
orchid: 0xda70d6ff,
|
||||
palegoldenrod: 0xeee8aaff,
|
||||
palegreen: 0x98fb98ff,
|
||||
paleturquoise: 0xafeeeeff,
|
||||
palevioletred: 0xdb7093ff,
|
||||
papayawhip: 0xffefd5ff,
|
||||
peachpuff: 0xffdab9ff,
|
||||
peru: 0xcd853fff,
|
||||
pink: 0xffc0cbff,
|
||||
plum: 0xdda0ddff,
|
||||
powderblue: 0xb0e0e6ff,
|
||||
purple: 0x800080ff,
|
||||
rebeccapurple: 0x663399ff,
|
||||
red: 0xff0000ff,
|
||||
rosybrown: 0xbc8f8fff,
|
||||
royalblue: 0x4169e1ff,
|
||||
saddlebrown: 0x8b4513ff,
|
||||
salmon: 0xfa8072ff,
|
||||
sandybrown: 0xf4a460ff,
|
||||
seagreen: 0x2e8b57ff,
|
||||
seashell: 0xfff5eeff,
|
||||
sienna: 0xa0522dff,
|
||||
silver: 0xc0c0c0ff,
|
||||
skyblue: 0x87ceebff,
|
||||
slateblue: 0x6a5acdff,
|
||||
slategray: 0x708090ff,
|
||||
slategrey: 0x708090ff,
|
||||
snow: 0xfffafaff,
|
||||
springgreen: 0x00ff7fff,
|
||||
steelblue: 0x4682b4ff,
|
||||
tan: 0xd2b48cff,
|
||||
teal: 0x008080ff,
|
||||
thistle: 0xd8bfd8ff,
|
||||
tomato: 0xff6347ff,
|
||||
turquoise: 0x40e0d0ff,
|
||||
violet: 0xee82eeff,
|
||||
wheat: 0xf5deb3ff,
|
||||
white: 0xffffffff,
|
||||
whitesmoke: 0xf5f5f5ff,
|
||||
yellow: 0xffff00ff,
|
||||
yellowgreen: 0x9acd32ff
|
||||
}
|
||||
|
||||
module.exports = normalizeColor
|
|
@ -0,0 +1,15 @@
|
|||
{
|
||||
"name":"<%= projectName %>",
|
||||
"main": "./bin/crna-entry.js",
|
||||
"dependencies": {
|
||||
"@tarojs/components-rn": "^<%= version %>",
|
||||
"@tarojs/taro-rn": "^<%= version %>",
|
||||
"@tarojs/taro-router-rn": "^<%= version %>",
|
||||
"@tarojs/taro-redux-rn": "^<%= version %>",
|
||||
"expo": "27.0.1",
|
||||
"react": "16.3.1",
|
||||
"react-native": "0.55.2",
|
||||
"redux": "^4.0.0",
|
||||
"tslib": "^1.8.0"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,109 @@
|
|||
const path = require('path')
|
||||
const fs = require('fs-extra')
|
||||
const postcss = require('postcss')
|
||||
const chalk = require('chalk')
|
||||
const pxtransform = require('postcss-pxtransform')
|
||||
const transformCSS = require('css-to-react-native-transform').default
|
||||
const {StyleSheetValidation} = require('./StyleSheet/index')
|
||||
const Util = require('../util')
|
||||
const npmProcess = require('../util/npm')
|
||||
|
||||
const DEVICE_RATIO = 'deviceRatio'
|
||||
|
||||
/**
|
||||
* @description 读取 css/scss/less 文件,预处理后,返回 css string
|
||||
* @param {string}filePath
|
||||
* @param {object} pluginsConfig
|
||||
* @returns {*}
|
||||
*/
|
||||
function loadStyle ({filePath, pluginsConfig}) {
|
||||
const fileExt = path.extname(filePath)
|
||||
const pluginName = Util.FILE_PROCESSOR_MAP[fileExt]
|
||||
if (pluginName) {
|
||||
return npmProcess.callPlugin(pluginName, null, filePath, pluginsConfig[pluginName] || {})
|
||||
.then((item) => {
|
||||
return {
|
||||
css: item.css.toString(),
|
||||
filePath
|
||||
}
|
||||
}).catch((e) => {
|
||||
Util.printLog(Util.pocessTypeEnum.ERROR, '样式预处理', filePath)
|
||||
console.log(e.stack)
|
||||
})
|
||||
}
|
||||
return new Promise((resolve, reject) => {
|
||||
fs.readFile(filePath, 'utf-8', (err, content) => {
|
||||
if (err) {
|
||||
return reject(err)
|
||||
}
|
||||
resolve({
|
||||
css: content,
|
||||
filePath
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 传入 css string ,返回 postCSS 处理后的 css string
|
||||
* @param {string} css
|
||||
* @param {string} filePath
|
||||
* @param {object} projectConfig
|
||||
* @returns {Function | any}
|
||||
*/
|
||||
function postCSS ({css, filePath, projectConfig}) {
|
||||
let pxTransformConfig = {
|
||||
designWidth: projectConfig.designWidth || 750
|
||||
}
|
||||
if (projectConfig.hasOwnProperty(DEVICE_RATIO)) {
|
||||
pxTransformConfig[DEVICE_RATIO] = projectConfig.deviceRatio
|
||||
}
|
||||
return postcss(pxtransform({
|
||||
platform: 'rn',
|
||||
...pxTransformConfig
|
||||
}))
|
||||
.process(css, {from: filePath})
|
||||
.then((result) => {
|
||||
return {
|
||||
css: result.css,
|
||||
filePath
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function getStyleObject ({css, filePath}) {
|
||||
var styleObject = {}
|
||||
try {
|
||||
styleObject = transformCSS(css)
|
||||
} catch (err) {
|
||||
Util.printLog(Util.pocessTypeEnum.WARNING, 'css-to-react-native 报错', filePath)
|
||||
console.log(chalk.red(err.stack))
|
||||
}
|
||||
return styleObject
|
||||
}
|
||||
|
||||
function validateStyle ({styleObject, filePath}) {
|
||||
for (let name in styleObject) {
|
||||
try {
|
||||
StyleSheetValidation.validateStyle(name, styleObject)
|
||||
} catch (err) {
|
||||
Util.printLog(Util.pocessTypeEnum.WARNING, '样式不支持', filePath)
|
||||
console.log(chalk.red(err.message))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function writeStyleFile ({css, tempFilePath}) {
|
||||
const fileContent = `import { StyleSheet } from 'react-native'\n\nexport default StyleSheet.create(${css})`
|
||||
fs.ensureDirSync(path.dirname(tempFilePath))
|
||||
fs.writeFileSync(tempFilePath, fileContent)
|
||||
Util.printLog(Util.pocessTypeEnum.GENERATE, '生成文件', tempFilePath)
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
loadStyle,
|
||||
postCSS,
|
||||
getStyleObject,
|
||||
validateStyle,
|
||||
writeStyleFile
|
||||
}
|
|
@ -0,0 +1,489 @@
|
|||
const path = require('path')
|
||||
const babel = require('babel-core')
|
||||
const traverse = require('babel-traverse').default
|
||||
const t = require('babel-types')
|
||||
const _ = require('lodash')
|
||||
const generate = require('babel-generator').default
|
||||
const template = require('babel-template')
|
||||
const wxTransformer = require('../../../taro-transformer-wx/lib/src/index.js').default
|
||||
const Util = require('../util')
|
||||
const babylonConfig = require('../config/babylon')
|
||||
const {source: toAst} = require('../util/ast_convert')
|
||||
|
||||
const reactImportDefaultName = 'React'
|
||||
let taroImportDefaultName // import default from @tarojs/taro
|
||||
let componentClassName // get app.js class name
|
||||
const providerComponentName = 'Provider'
|
||||
const setStoreFuncName = 'setStore'
|
||||
const routerImportDefaultName = 'TaroRouter'
|
||||
const DEVICE_RATIO = 'deviceRatio'
|
||||
|
||||
const taroApis = [
|
||||
'getEnv',
|
||||
'ENV_TYPE',
|
||||
'eventCenter',
|
||||
'Events',
|
||||
'internal_safe_get',
|
||||
'internal_dynamic_recursive'
|
||||
]
|
||||
|
||||
const PACKAGES = {
|
||||
'@tarojs/taro': '@tarojs/taro',
|
||||
'@tarojs/taro-rn': '@tarojs/taro-rn',
|
||||
'@tarojs/taro-router-rn': '@tarojs/taro-router-rn',
|
||||
'@tarojs/redux': '@tarojs/redux',
|
||||
'@tarojs/components': '@tarojs/components',
|
||||
'@tarojs/components-rn': '@tarojs/components-rn',
|
||||
'react': 'react',
|
||||
'react-native': 'react-native',
|
||||
'react-redux-rn': '@tarojs/taro-redux-rn',
|
||||
'@tarojs/mobx': '@tarojs/mobx',
|
||||
'@tarojs/mobx-rn': '@tarojs/mobx-rn'
|
||||
}
|
||||
|
||||
function getInitPxTransformNode (projectConfig) {
|
||||
let pxTransformConfig = {designWidth: projectConfig.designWidth || 750}
|
||||
|
||||
if (projectConfig.hasOwnProperty(DEVICE_RATIO)) {
|
||||
pxTransformConfig[DEVICE_RATIO] = projectConfig.deviceRatio
|
||||
}
|
||||
const initPxTransformNode = toAst(`Taro.initPxTransform(${JSON.stringify(pxTransformConfig)})`)
|
||||
return initPxTransformNode
|
||||
}
|
||||
|
||||
function getClassPropertyVisitor ({filePath, pages, iconPaths, isEntryFile}) {
|
||||
return (astPath) => {
|
||||
const node = astPath.node
|
||||
const key = node.key
|
||||
const value = node.value
|
||||
if (key.name !== 'config' || !t.isObjectExpression(value)) return
|
||||
// 入口文件的 config ,与页面的分开处理
|
||||
if (isEntryFile) {
|
||||
// 读取 config 配置
|
||||
astPath.traverse({
|
||||
ObjectProperty (astPath) {
|
||||
const node = astPath.node
|
||||
const key = node.key
|
||||
const value = node.value
|
||||
// if (key.name !== 'pages' || !t.isArrayExpression(value)) return
|
||||
if (key.name === 'pages' && t.isArrayExpression(value)) {
|
||||
// 分包
|
||||
let root = ''
|
||||
const rootNode = astPath.parent.properties.find(v => {
|
||||
return v.key.name === 'root'
|
||||
})
|
||||
root = rootNode ? rootNode.value.value : ''
|
||||
|
||||
value.elements.forEach(v => {
|
||||
const pagePath = `${root}/${v.value}`.replace(/\/{2,}/g, '/')
|
||||
pages.push(pagePath.replace(/^\//, ''))
|
||||
})
|
||||
astPath.remove()
|
||||
}
|
||||
// window
|
||||
if (key.name === 'window' && t.isObjectExpression(value)) {
|
||||
return
|
||||
}
|
||||
if (key.name === 'tabBar' && t.isObjectExpression(value)) {
|
||||
astPath.traverse({
|
||||
ObjectProperty (astPath) {
|
||||
let node = astPath.node
|
||||
let value = node.value.value
|
||||
if (node.key.name === 'iconPath' ||
|
||||
node.key.value === 'iconPath' ||
|
||||
node.key.name === 'selectedIconPath' ||
|
||||
node.key.value === 'selectedIconPath'
|
||||
) {
|
||||
if (typeof value !== 'string') return
|
||||
let iconName = _.camelCase(value.split('/'))
|
||||
if (iconPaths.indexOf(value) === -1) {
|
||||
iconPaths.push(value)
|
||||
}
|
||||
astPath.insertAfter(t.objectProperty(
|
||||
t.identifier(node.key.name || node.key.value),
|
||||
t.identifier(iconName)
|
||||
))
|
||||
astPath.remove()
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
astPath.node.static = 'true'
|
||||
}
|
||||
}
|
||||
|
||||
function getJSAst (code, filePath) {
|
||||
return wxTransformer({
|
||||
code,
|
||||
sourcePath: filePath,
|
||||
isNormal: true,
|
||||
isTyped: Util.REG_TYPESCRIPT.test(filePath),
|
||||
adapter: 'rn'
|
||||
}).ast
|
||||
}
|
||||
|
||||
/**
|
||||
* TS 编译器会把 class property 移到构造器,
|
||||
* 而小程序要求 `config` 和所有函数在初始化(after new Class)之后就收集到所有的函数和 config 信息,
|
||||
* 所以当如构造器里有 this.func = () => {...} 的形式,就给他转换成普通的 classProperty function
|
||||
* 如果有 config 就给他还原
|
||||
*/
|
||||
function resetTSClassProperty (body) {
|
||||
for (const method of body) {
|
||||
if (t.isClassMethod(method) && method.kind === 'constructor') {
|
||||
for (const statement of _.cloneDeep(method.body.body)) {
|
||||
if (t.isExpressionStatement(statement) && t.isAssignmentExpression(statement.expression)) {
|
||||
const expr = statement.expression
|
||||
const {left, right} = expr
|
||||
if (
|
||||
t.isMemberExpression(left) &&
|
||||
t.isThisExpression(left.object) &&
|
||||
t.isIdentifier(left.property)
|
||||
) {
|
||||
if (
|
||||
(t.isArrowFunctionExpression(right) || t.isFunctionExpression(right)) ||
|
||||
(left.property.name === 'config' && t.isObjectExpression(right))
|
||||
) {
|
||||
body.push(
|
||||
t.classProperty(left.property, right)
|
||||
)
|
||||
_.remove(method.body.body, statement)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const ClassDeclarationOrExpression = {
|
||||
enter (astPath) {
|
||||
const node = astPath.node
|
||||
if (!node.superClass) return
|
||||
if (
|
||||
node.superClass.type === 'MemberExpression' &&
|
||||
node.superClass.object.name === taroImportDefaultName
|
||||
) {
|
||||
node.superClass.object.name = taroImportDefaultName
|
||||
if (node.id === null) {
|
||||
const renameComponentClassName = '_TaroComponentClass'
|
||||
componentClassName = renameComponentClassName
|
||||
astPath.replaceWith(
|
||||
t.classDeclaration(
|
||||
t.identifier(renameComponentClassName),
|
||||
node.superClass,
|
||||
node.body,
|
||||
node.decorators || []
|
||||
)
|
||||
)
|
||||
} else {
|
||||
componentClassName = node.id.name
|
||||
}
|
||||
} else if (node.superClass.name === 'Component') {
|
||||
resetTSClassProperty(node.body.body)
|
||||
if (node.id === null) {
|
||||
const renameComponentClassName = '_TaroComponentClass'
|
||||
componentClassName = renameComponentClassName
|
||||
astPath.replaceWith(
|
||||
t.classDeclaration(
|
||||
t.identifier(renameComponentClassName),
|
||||
node.superClass,
|
||||
node.body,
|
||||
node.decorators || []
|
||||
)
|
||||
)
|
||||
} else {
|
||||
componentClassName = node.id.name
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function parseJSCode ({code, filePath, isEntryFile, projectConfig}) {
|
||||
let ast
|
||||
try {
|
||||
ast = getJSAst(code, filePath)
|
||||
} catch (e) {
|
||||
throw e
|
||||
}
|
||||
const styleFiles = []
|
||||
let pages = [] // app.js 里面的config 配置里面的 pages
|
||||
let iconPaths = [] // app.js 里面的config 配置里面的需要引入的 iconPath
|
||||
let hasAddReactImportDefaultName = false
|
||||
let providorImportName
|
||||
let storeName
|
||||
let hasAppExportDefault
|
||||
let classRenderReturnJSX
|
||||
|
||||
traverse(ast, {
|
||||
ClassExpression: ClassDeclarationOrExpression,
|
||||
ClassDeclaration: ClassDeclarationOrExpression,
|
||||
ImportDeclaration (astPath) {
|
||||
const node = astPath.node
|
||||
const source = node.source
|
||||
let value = source.value
|
||||
const valueExtname = path.extname(value)
|
||||
const specifiers = node.specifiers
|
||||
const pathAlias = projectConfig.alias || {}
|
||||
if (Util.isAliasPath(value, pathAlias)) {
|
||||
source.value = value = Util.replaceAliasPath(filePath, value, pathAlias)
|
||||
}
|
||||
// 引入的包为 npm 包
|
||||
if (!Util.isNpmPkg(value)) {
|
||||
// import 样式处理
|
||||
if (Util.REG_STYLE.test(valueExtname)) {
|
||||
const stylePath = path.resolve(path.dirname(filePath), value)
|
||||
if (styleFiles.indexOf(stylePath) < 0) {
|
||||
styleFiles.push(stylePath)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
if (value === PACKAGES['@tarojs/taro']) {
|
||||
let specifier = specifiers.find(item => item.type === 'ImportDefaultSpecifier')
|
||||
if (specifier) {
|
||||
hasAddReactImportDefaultName = true
|
||||
taroImportDefaultName = specifier.local.name
|
||||
specifier.local.name = reactImportDefaultName
|
||||
} else if (!hasAddReactImportDefaultName) {
|
||||
hasAddReactImportDefaultName = true
|
||||
node.specifiers.unshift(
|
||||
t.importDefaultSpecifier(t.identifier(reactImportDefaultName))
|
||||
)
|
||||
}
|
||||
// 删除从@tarojs/taro引入的 React
|
||||
specifiers.forEach((item, index) => {
|
||||
if (item.type === 'ImportDefaultSpecifier') {
|
||||
specifiers.splice(index, 1)
|
||||
}
|
||||
})
|
||||
const taroApisSpecifiers = []
|
||||
specifiers.forEach((item, index) => {
|
||||
if (item.imported && taroApis.indexOf(item.imported.name) >= 0) {
|
||||
taroApisSpecifiers.push(t.importSpecifier(t.identifier(item.local.name), t.identifier(item.imported.name)))
|
||||
specifiers.splice(index, 1)
|
||||
}
|
||||
})
|
||||
source.value = PACKAGES['@tarojs/taro-rn']
|
||||
// insert React
|
||||
astPath.insertBefore(template(`import React from 'react'`, babylonConfig)())
|
||||
|
||||
if (taroApisSpecifiers.length) {
|
||||
astPath.insertBefore(t.importDeclaration(taroApisSpecifiers, t.stringLiteral(PACKAGES['@tarojs/taro-rn'])))
|
||||
}
|
||||
if (!specifiers.length) {
|
||||
astPath.remove()
|
||||
}
|
||||
} else if (value === PACKAGES['@tarojs/redux']) {
|
||||
const specifier = specifiers.find(item => {
|
||||
return t.isImportSpecifier(item) && item.imported.name === providerComponentName
|
||||
})
|
||||
if (specifier) {
|
||||
providorImportName = specifier.local.name
|
||||
} else {
|
||||
providorImportName = providerComponentName
|
||||
specifiers.push(t.importSpecifier(t.identifier(providerComponentName), t.identifier(providerComponentName)))
|
||||
}
|
||||
source.value = PACKAGES['react-redux-rn']
|
||||
} else if (value === PACKAGES['@tarojs/mobx']) {
|
||||
const specifier = specifiers.find(item => {
|
||||
return t.isImportSpecifier(item) && item.imported.name === providerComponentName
|
||||
})
|
||||
if (specifier) {
|
||||
providorImportName = specifier.local.name
|
||||
} else {
|
||||
providorImportName = providerComponentName
|
||||
specifiers.push(t.importSpecifier(t.identifier(providerComponentName), t.identifier(providerComponentName)))
|
||||
}
|
||||
source.value = PACKAGES['@tarojs/mobx-rn']
|
||||
} else if (value === PACKAGES['@tarojs/components']) {
|
||||
source.value = PACKAGES['@tarojs/components-rn']
|
||||
}
|
||||
},
|
||||
ClassProperty: getClassPropertyVisitor({filePath, pages, iconPaths, isEntryFile}),
|
||||
// 获取 classRenderReturnJSX
|
||||
ClassMethod (astPath) {
|
||||
let node = astPath.node
|
||||
const key = node.key
|
||||
if (key.name !== 'render' || !isEntryFile) return
|
||||
astPath.traverse({
|
||||
BlockStatement (astPath) {
|
||||
if (astPath.parent === node) {
|
||||
node = astPath.node
|
||||
astPath.traverse({
|
||||
ReturnStatement (astPath) {
|
||||
if (astPath.parent === node) {
|
||||
astPath.traverse({
|
||||
JSXElement (astPath) {
|
||||
classRenderReturnJSX = generate(astPath.node).code
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
ExportDefaultDeclaration () {
|
||||
if (isEntryFile) {
|
||||
hasAppExportDefault = true
|
||||
}
|
||||
},
|
||||
JSXOpeningElement: {
|
||||
enter (astPath) {
|
||||
if (astPath.node.name.name === 'Provider') {
|
||||
for (let v of astPath.node.attributes) {
|
||||
if (v.name.name !== 'store') continue
|
||||
storeName = v.value.expression.name
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
Program: {
|
||||
exit (astPath) {
|
||||
const node = astPath.node
|
||||
astPath.traverse({
|
||||
ClassMethod (astPath) {
|
||||
const node = astPath.node
|
||||
const key = node.key
|
||||
if (key.name !== 'render' || !isEntryFile) return
|
||||
let funcBody = classRenderReturnJSX
|
||||
if (pages.length > 0) {
|
||||
funcBody = `<RootStack/>`
|
||||
}
|
||||
if (providerComponentName && storeName) {
|
||||
// 使用redux 或 mobx
|
||||
funcBody = `
|
||||
<${providorImportName} store={${storeName}}>
|
||||
${funcBody}
|
||||
</${providorImportName}>`
|
||||
}
|
||||
node.body = template(`{return (${funcBody});}`, babylonConfig)()
|
||||
},
|
||||
|
||||
CallExpression (astPath) {
|
||||
const node = astPath.node
|
||||
const callee = node.callee
|
||||
const calleeName = callee.name
|
||||
const parentPath = astPath.parentPath
|
||||
|
||||
if (t.isMemberExpression(callee)) {
|
||||
if (callee.object.name === taroImportDefaultName && callee.property.name === 'render') {
|
||||
astPath.remove()
|
||||
}
|
||||
} else {
|
||||
if (calleeName === setStoreFuncName) {
|
||||
if (parentPath.isAssignmentExpression() ||
|
||||
parentPath.isExpressionStatement() ||
|
||||
parentPath.isVariableDeclarator()) {
|
||||
parentPath.remove()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
// import Taro from @tarojs/taro-rn
|
||||
if (taroImportDefaultName) {
|
||||
const importTaro = template(
|
||||
`import ${taroImportDefaultName} from '${PACKAGES['@tarojs/taro-rn']}'`,
|
||||
babylonConfig
|
||||
)()
|
||||
node.body.unshift(importTaro)
|
||||
}
|
||||
|
||||
if (isEntryFile) {
|
||||
// 注入 import page from 'XXX'
|
||||
pages.forEach(item => {
|
||||
const pagePath = item.startsWith('/') ? item : `/${item}`
|
||||
const screenName = _.camelCase(pagePath.split('/'), {pascalCase: true})
|
||||
const importScreen = template(
|
||||
`import ${screenName} from '.${pagePath}'`,
|
||||
babylonConfig
|
||||
)()
|
||||
node.body.unshift(importScreen)
|
||||
})
|
||||
iconPaths.forEach(item => {
|
||||
const iconPath = item.startsWith('/') ? item : `/${item}`
|
||||
const iconName = _.camelCase(iconPath.split('/'))
|
||||
const importIcon = template(
|
||||
`import ${iconName} from '.${iconPath}'`,
|
||||
babylonConfig
|
||||
)()
|
||||
node.body.unshift(importIcon)
|
||||
})
|
||||
// Taro.initRouter 生成 RootStack
|
||||
const routerPages = pages
|
||||
.map(item => {
|
||||
const pagePath = item.startsWith('/') ? item : `/${item}`
|
||||
const screenName = _.camelCase(pagePath.split('/'), {pascalCase: true})
|
||||
return `['${item}',${screenName}]`
|
||||
})
|
||||
.join(',')
|
||||
node.body.push(template(
|
||||
`const RootStack = ${routerImportDefaultName}.initRouter(
|
||||
[${routerPages}],
|
||||
${taroImportDefaultName},
|
||||
App.config
|
||||
)`,
|
||||
babylonConfig
|
||||
)())
|
||||
// initNativeApi
|
||||
const initNativeApi = template(
|
||||
`${taroImportDefaultName}.initNativeApi(${taroImportDefaultName})`,
|
||||
babylonConfig
|
||||
)()
|
||||
node.body.push(initNativeApi)
|
||||
|
||||
// import @tarojs/taro-router-rn
|
||||
const importTaroRouter = template(
|
||||
`import TaroRouter from '${PACKAGES['@tarojs/taro-router-rn']}'`,
|
||||
babylonConfig
|
||||
)()
|
||||
node.body.unshift(importTaroRouter)
|
||||
|
||||
// Taro.initPxTransform
|
||||
node.body.push(getInitPxTransformNode(projectConfig))
|
||||
|
||||
// export default App
|
||||
if (!hasAppExportDefault) {
|
||||
const appExportDefault = template(
|
||||
`export default ${componentClassName}`,
|
||||
babylonConfig
|
||||
)()
|
||||
node.body.push(appExportDefault)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
try {
|
||||
const constantsReplaceList = Object.assign({
|
||||
'process.env.TARO_ENV': Util.BUILD_TYPES.RN
|
||||
}, Util.generateEnvList(projectConfig.env || {}), Util.generateConstantsList(projectConfig.defineConstants || {}))
|
||||
// TODO 使用 babel-plugin-transform-jsx-to-stylesheet 处理 JSX 里面样式的处理,删除无效的样式引入待优化
|
||||
ast = babel.transformFromAst(ast, code, {
|
||||
plugins: [
|
||||
[require('babel-plugin-transform-jsx-to-stylesheet'), {filePath}],
|
||||
require('babel-plugin-transform-decorators-legacy').default,
|
||||
require('babel-plugin-transform-class-properties'),
|
||||
[require('babel-plugin-danger-remove-unused-import'), {ignore: ['@tarojs/taro', 'react', 'react-native', 'nervjs']}],
|
||||
[require('babel-plugin-transform-define').default, constantsReplaceList]
|
||||
]
|
||||
}).ast
|
||||
} catch (e) {
|
||||
throw e
|
||||
}
|
||||
|
||||
return {
|
||||
code: unescape(generate(ast).code.replace(/\\u/g, '%u')),
|
||||
styleFiles
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {transformJSCode: parseJSCode}
|
|
@ -0,0 +1,431 @@
|
|||
const fs = require('fs-extra')
|
||||
const path = require('path')
|
||||
const chokidar = require('chokidar')
|
||||
const chalk = require('chalk')
|
||||
const wxTransformer = require('../../taro-transformer-wx/lib/src/index.js').default
|
||||
const traverse = require('babel-traverse').default
|
||||
const t = require('babel-types')
|
||||
const generate = require('babel-generator').default
|
||||
const _ = require('lodash')
|
||||
|
||||
const { processFiles } = require('./h5')
|
||||
const npmProcess = require('./util/npm')
|
||||
|
||||
const CONFIG = require('./config')
|
||||
const {
|
||||
resolveScriptPath,
|
||||
resolveStylePath,
|
||||
printLog,
|
||||
pocessTypeEnum,
|
||||
PROJECT_CONFIG,
|
||||
BUILD_TYPES,
|
||||
REG_STYLE,
|
||||
REG_TYPESCRIPT,
|
||||
cssImports
|
||||
} = require('./util')
|
||||
|
||||
const appPath = process.cwd()
|
||||
const configDir = path.join(appPath, PROJECT_CONFIG)
|
||||
const projectConfig = require(configDir)(_.merge)
|
||||
const sourceDirName = projectConfig.sourceRoot || CONFIG.SOURCE_DIR
|
||||
let outputDirName = projectConfig.outputRoot || CONFIG.OUTPUT_DIR
|
||||
const sourceDir = path.join(appPath, sourceDirName)
|
||||
const entryFilePath = resolveScriptPath(path.join(sourceDir, 'index'))
|
||||
const entryFileName = path.basename(entryFilePath)
|
||||
const tempDir = '.temp'
|
||||
const tempPath = path.join(appPath, tempDir)
|
||||
|
||||
const weappOutputName = 'weapp'
|
||||
const h5OutputName = 'h5'
|
||||
|
||||
async function buildH5Script () {
|
||||
const h5Config = projectConfig.h5 || {}
|
||||
const entryFile = path.basename(entryFileName, path.extname(entryFileName)) + '.js'
|
||||
outputDirName = `${outputDirName}/${h5OutputName}`
|
||||
h5Config.env = projectConfig.env
|
||||
h5Config.defineConstants = projectConfig.defineConstants
|
||||
h5Config.plugins = projectConfig.plugins
|
||||
h5Config.designWidth = projectConfig.designWidth
|
||||
if (projectConfig.deviceRatio) {
|
||||
h5Config.deviceRatio = projectConfig.deviceRatio
|
||||
}
|
||||
h5Config.sourceRoot = sourceDirName
|
||||
h5Config.outputRoot = outputDirName
|
||||
h5Config.entry = Object.assign({
|
||||
app: [path.join(tempPath, entryFile)]
|
||||
}, h5Config.entry)
|
||||
h5Config.isWatch = false
|
||||
const webpackRunner = await npmProcess.getNpmPkg('@tarojs/webpack-runner')
|
||||
webpackRunner(h5Config)
|
||||
}
|
||||
|
||||
async function buildH5Lib () {
|
||||
try {
|
||||
const outputDir = path.join(appPath, outputDirName, h5OutputName)
|
||||
const tempEntryFilePath = resolveScriptPath(path.join(tempPath, 'index'))
|
||||
const outputEntryFilePath = path.join(outputDir, path.basename(tempEntryFilePath))
|
||||
const code = fs.readFileSync(tempEntryFilePath).toString()
|
||||
const transformResult = wxTransformer({
|
||||
code,
|
||||
sourcePath: tempEntryFilePath,
|
||||
outputPath: outputEntryFilePath,
|
||||
isNormal: true,
|
||||
isTyped: REG_TYPESCRIPT.test(tempEntryFilePath)
|
||||
})
|
||||
const { styleFiles, components, code: generateCode } = parseEntryAst(transformResult.ast, tempEntryFilePath)
|
||||
const relativePath = path.relative(appPath, tempEntryFilePath)
|
||||
printLog(pocessTypeEnum.COPY, '发现文件', relativePath)
|
||||
fs.ensureDirSync(path.dirname(outputEntryFilePath))
|
||||
fs.writeFileSync(outputEntryFilePath, generateCode)
|
||||
if (components.length) {
|
||||
components.forEach(item => {
|
||||
copyFileToDist(item.path, tempPath, outputDir)
|
||||
})
|
||||
analyzeFiles(components.map(item => item.path), tempPath, outputDir)
|
||||
}
|
||||
if (styleFiles.length) {
|
||||
styleFiles.forEach(item => {
|
||||
copyFileToDist(item, tempPath, path.join(appPath, outputDirName))
|
||||
})
|
||||
analyzeStyleFilesImport(styleFiles, tempPath, path.join(appPath, outputDirName))
|
||||
}
|
||||
} catch (err) {
|
||||
console.log(err)
|
||||
}
|
||||
}
|
||||
|
||||
function copyFileToDist (filePath, sourceDir, outputDir) {
|
||||
if (!path.isAbsolute(filePath)) {
|
||||
return
|
||||
}
|
||||
const dirname = path.dirname(filePath)
|
||||
const distDirname = dirname.replace(sourceDir, outputDir)
|
||||
const relativePath = path.relative(appPath, filePath)
|
||||
printLog(pocessTypeEnum.COPY, '发现文件', relativePath)
|
||||
fs.ensureDirSync(distDirname)
|
||||
fs.copyFileSync(filePath, path.format({
|
||||
dir: distDirname,
|
||||
base: path.basename(filePath)
|
||||
}))
|
||||
}
|
||||
|
||||
function parseEntryAst (ast, relativeFile) {
|
||||
const styleFiles = []
|
||||
const components = []
|
||||
const importExportName = []
|
||||
let exportDefaultName = null
|
||||
|
||||
traverse(ast, {
|
||||
ExportNamedDeclaration (astPath) {
|
||||
const node = astPath.node
|
||||
const specifiers = node.specifiers
|
||||
const source = node.source
|
||||
if (source && source.type === 'StringLiteral') {
|
||||
specifiers.forEach(specifier => {
|
||||
const exported = specifier.exported
|
||||
components.push({
|
||||
name: exported.name,
|
||||
path: resolveScriptPath(path.resolve(path.dirname(relativeFile), source.value))
|
||||
})
|
||||
})
|
||||
} else {
|
||||
specifiers.forEach(specifier => {
|
||||
const exported = specifier.exported
|
||||
importExportName.push(exported.name)
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
ExportDefaultDeclaration (astPath) {
|
||||
const node = astPath.node
|
||||
const declaration = node.declaration
|
||||
if (t.isIdentifier(declaration)) {
|
||||
exportDefaultName = declaration.name
|
||||
}
|
||||
},
|
||||
|
||||
Program: {
|
||||
exit (astPath) {
|
||||
astPath.traverse({
|
||||
ImportDeclaration (astPath) {
|
||||
const node = astPath.node
|
||||
const specifiers = node.specifiers
|
||||
const source = node.source
|
||||
const value = source.value
|
||||
const valueExtname = path.extname(value)
|
||||
if (REG_STYLE.test(valueExtname)) {
|
||||
const stylePath = path.resolve(path.dirname(relativeFile), value)
|
||||
if (styleFiles.indexOf(stylePath) < 0) {
|
||||
styleFiles.push(stylePath)
|
||||
}
|
||||
astPath.remove()
|
||||
} else {
|
||||
if (importExportName.length) {
|
||||
importExportName.forEach(nameItem => {
|
||||
specifiers.forEach(specifier => {
|
||||
const local = specifier.local
|
||||
if (local.name === nameItem) {
|
||||
components.push({
|
||||
name: local.name,
|
||||
path: resolveScriptPath(path.resolve(path.dirname(relativeFile), source.value))
|
||||
})
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
if (exportDefaultName != null) {
|
||||
specifiers.forEach(specifier => {
|
||||
const local = specifier.local
|
||||
if (local.name === exportDefaultName) {
|
||||
components.push({
|
||||
name: local.name,
|
||||
path: resolveScriptPath(path.resolve(path.dirname(relativeFile), source.value))
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
const code = generate(ast).code
|
||||
return {
|
||||
code,
|
||||
styleFiles,
|
||||
components
|
||||
}
|
||||
}
|
||||
|
||||
function analyzeFiles (files, sourceDir, outputDir) {
|
||||
const { parseAst } = require('./weapp')
|
||||
files.forEach(file => {
|
||||
if (fs.existsSync(file)) {
|
||||
const code = fs.readFileSync(file).toString()
|
||||
const transformResult = wxTransformer({
|
||||
code,
|
||||
sourcePath: file,
|
||||
outputPath: file,
|
||||
isNormal: true,
|
||||
isTyped: REG_TYPESCRIPT.test(file)
|
||||
})
|
||||
const {
|
||||
styleFiles,
|
||||
scriptFiles,
|
||||
jsonFiles,
|
||||
mediaFiles
|
||||
} = parseAst('NORMAL', transformResult.ast, [], file, file, true)
|
||||
const resFiles = styleFiles.concat(scriptFiles, jsonFiles, mediaFiles)
|
||||
if (resFiles.length) {
|
||||
resFiles.forEach(item => {
|
||||
copyFileToDist(item, sourceDir, outputDir)
|
||||
})
|
||||
}
|
||||
if (scriptFiles.length) {
|
||||
analyzeFiles(scriptFiles, sourceDir, outputDir)
|
||||
}
|
||||
if (styleFiles.length) {
|
||||
analyzeStyleFilesImport(styleFiles, sourceDir, outputDir)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function analyzeStyleFilesImport (styleFiles, sourceDir, outputDir) {
|
||||
styleFiles.forEach(item => {
|
||||
if (!fs.existsSync(item)) {
|
||||
return
|
||||
}
|
||||
let content = fs.readFileSync(item).toString()
|
||||
content = content.replace(/(?:@import\s+)?\burl\s*\(\s*("(?:[^\\"\r\n\f]|\\[\s\S])*"|'(?:[^\\'\n\r\f]|\\[\s\S])*'|[^)}\s]+)\s*\)(\s*;?)/g, (m, $1) => {
|
||||
if ($1) {
|
||||
let filePath = $1.replace(/'?"?/g, '')
|
||||
if (filePath.indexOf('.') === 0) {
|
||||
filePath = path.resolve(path.dirname(item), filePath)
|
||||
copyFileToDist(filePath, sourceDir, outputDir)
|
||||
}
|
||||
}
|
||||
return m
|
||||
})
|
||||
let imports = cssImports(content)
|
||||
if (imports.length > 0) {
|
||||
imports = imports.map(importItem => {
|
||||
const filePath = resolveStylePath(path.resolve(path.dirname(item), importItem))
|
||||
copyFileToDist(filePath, sourceDir, outputDir)
|
||||
return filePath
|
||||
})
|
||||
analyzeStyleFilesImport(imports, sourceDir, outputDir)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
async function buildForWeapp () {
|
||||
console.log()
|
||||
console.log(chalk.green('开始编译小程序端组件库!'))
|
||||
if (!fs.existsSync(entryFilePath)) {
|
||||
console.log(chalk.red('入口文件不存在,请检查!'))
|
||||
return
|
||||
}
|
||||
try {
|
||||
const { compileDepStyles } = require('./weapp')
|
||||
const outputDir = path.join(appPath, outputDirName, weappOutputName)
|
||||
const outputEntryFilePath = path.join(outputDir, entryFileName)
|
||||
const code = fs.readFileSync(entryFilePath).toString()
|
||||
const transformResult = wxTransformer({
|
||||
code,
|
||||
sourcePath: entryFilePath,
|
||||
outputPath: outputEntryFilePath,
|
||||
isNormal: true,
|
||||
isTyped: REG_TYPESCRIPT.test(entryFilePath)
|
||||
})
|
||||
const { styleFiles, components } = parseEntryAst(transformResult.ast, entryFilePath)
|
||||
if (styleFiles.length) {
|
||||
const outputStylePath = path.join(outputDir, 'css', 'index.css')
|
||||
await compileDepStyles(outputStylePath, styleFiles, false)
|
||||
}
|
||||
const relativePath = path.relative(appPath, entryFilePath)
|
||||
printLog(pocessTypeEnum.COPY, '发现文件', relativePath)
|
||||
fs.ensureDirSync(path.dirname(outputEntryFilePath))
|
||||
fs.copyFileSync(entryFilePath, path.format({
|
||||
dir: path.dirname(outputEntryFilePath),
|
||||
base: path.basename(outputEntryFilePath)
|
||||
}))
|
||||
if (components.length) {
|
||||
components.forEach(item => {
|
||||
copyFileToDist(item.path, sourceDir, outputDir)
|
||||
})
|
||||
analyzeFiles(components.map(item => item.path), sourceDir, outputDir)
|
||||
}
|
||||
} catch (err) {
|
||||
console.log(err)
|
||||
}
|
||||
}
|
||||
|
||||
async function buildForH5 (buildConfig) {
|
||||
const { buildTemp } = require('./h5')
|
||||
console.log()
|
||||
console.log(chalk.green('开始编译 H5 端组件库!'))
|
||||
await buildTemp(buildConfig)
|
||||
if (process.env.TARO_BUILD_TYPE === 'script') {
|
||||
await buildH5Script()
|
||||
} else {
|
||||
await buildH5Lib()
|
||||
}
|
||||
}
|
||||
|
||||
function buildEntry () {
|
||||
const content = `if (process.env.TARO_ENV === '${BUILD_TYPES.H5}') {
|
||||
module.exports = require('./${h5OutputName}/index')
|
||||
module.exports.default = module.exports
|
||||
} else {
|
||||
module.exports = require('./${weappOutputName}/index')
|
||||
module.exports.default = module.exports
|
||||
}`
|
||||
const outputDir = path.join(appPath, outputDirName)
|
||||
fs.writeFileSync(path.join(outputDir, 'index.js'), content)
|
||||
}
|
||||
|
||||
function watchFiles () {
|
||||
console.log('\n', chalk.gray('监听文件修改中...'), '\n')
|
||||
|
||||
const watchList = [sourceDir]
|
||||
|
||||
const uiConfig = projectConfig.ui || {}
|
||||
const { extraWatchFiles = [] } = uiConfig
|
||||
extraWatchFiles.forEach(item => {
|
||||
watchList.push(path.join(appPath, item.path))
|
||||
if (typeof item.handler === 'function') item.callback = item.handler({ buildH5Script })
|
||||
})
|
||||
|
||||
const watcher = chokidar.watch(watchList, {
|
||||
ignored: /(^|[/\\])\../,
|
||||
ignoreInitial: true
|
||||
})
|
||||
|
||||
function syncWeappFile (filePath) {
|
||||
const outputDir = path.join(appPath, outputDirName, weappOutputName)
|
||||
copyFileToDist(filePath, sourceDir, outputDir)
|
||||
// 依赖分析
|
||||
const extname = path.extname(filePath)
|
||||
if (REG_STYLE.test(extname)) {
|
||||
analyzeStyleFilesImport([filePath], sourceDir, outputDir)
|
||||
} else {
|
||||
analyzeFiles([filePath], sourceDir, outputDir)
|
||||
}
|
||||
}
|
||||
|
||||
function syncH5File (filePath) {
|
||||
const outputDir = path.join(appPath, outputDirName, h5OutputName)
|
||||
const fileTempPath = filePath.replace(sourceDir, tempPath)
|
||||
processFiles(filePath)
|
||||
|
||||
if (process.env.TARO_BUILD_TYPE === 'script') {
|
||||
buildH5Script()
|
||||
} else {
|
||||
copyFileToDist(fileTempPath, tempPath, outputDir)
|
||||
// 依赖分析
|
||||
const extname = path.extname(filePath)
|
||||
if (REG_STYLE.test(extname)) {
|
||||
analyzeStyleFilesImport([fileTempPath], tempPath, outputDir)
|
||||
} else {
|
||||
analyzeFiles([fileTempPath], tempPath, outputDir)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function handleChange (filePath, type, tips) {
|
||||
const relativePath = path.relative(appPath, filePath)
|
||||
printLog(type, tips, relativePath)
|
||||
|
||||
let processed = false
|
||||
extraWatchFiles.forEach(item => {
|
||||
if (filePath.indexOf(item.path.substr(2)) < 0) return
|
||||
if (typeof item.callback === 'function') {
|
||||
item.callback()
|
||||
processed = true
|
||||
}
|
||||
})
|
||||
if (processed) return
|
||||
|
||||
try {
|
||||
syncWeappFile(filePath)
|
||||
syncH5File(filePath)
|
||||
} catch (err) {
|
||||
console.log(err)
|
||||
}
|
||||
}
|
||||
|
||||
watcher
|
||||
.on('add', filePath => handleChange(filePath, pocessTypeEnum.CREATE, '添加文件'))
|
||||
.on('change', filePath => handleChange(filePath, pocessTypeEnum.MODIFY, '文件变动'))
|
||||
.on('unlink', filePath => {
|
||||
for (const path in extraWatchFiles) {
|
||||
if (filePath.indexOf(path.substr(2)) > -1) return
|
||||
}
|
||||
|
||||
const relativePath = path.relative(appPath, filePath)
|
||||
printLog(pocessTypeEnum.UNLINK, '删除文件', relativePath)
|
||||
const weappOutputPath = path.join(appPath, outputDirName, weappOutputName)
|
||||
const h5OutputPath = path.join(appPath, outputDirName, h5OutputName)
|
||||
const fileTempPath = filePath.replace(sourceDir, tempPath)
|
||||
const fileWeappPath = filePath.replace(sourceDir, weappOutputPath)
|
||||
const fileH5Path = filePath.replace(sourceDir, h5OutputPath)
|
||||
fs.existsSync(fileTempPath) && fs.unlinkSync(fileTempPath)
|
||||
fs.existsSync(fileWeappPath) && fs.unlinkSync(fileWeappPath)
|
||||
fs.existsSync(fileH5Path) && fs.unlinkSync(fileH5Path)
|
||||
})
|
||||
}
|
||||
|
||||
async function build ({ watch }) {
|
||||
buildEntry()
|
||||
await buildForWeapp()
|
||||
await buildForH5()
|
||||
if (watch) {
|
||||
watchFiles()
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
build
|
||||
}
|
|
@ -0,0 +1,79 @@
|
|||
const t = require('babel-types')
|
||||
const babylonConfig = require('../config/babylon')
|
||||
const template = require('babel-template')
|
||||
const generate = require('better-babel-generator').default
|
||||
|
||||
function convertObjectToAstExpression (obj) {
|
||||
const objArr = Object.keys(obj).map(key => {
|
||||
const value = obj[key]
|
||||
if (typeof value === 'string') {
|
||||
return t.objectProperty(t.stringLiteral(key), t.stringLiteral(value))
|
||||
}
|
||||
if (typeof value === 'number') {
|
||||
return t.objectProperty(t.stringLiteral(key), t.numericLiteral(value))
|
||||
}
|
||||
if (typeof value === 'boolean') {
|
||||
return t.objectProperty(t.stringLiteral(key), t.booleanLiteral(value))
|
||||
}
|
||||
if (Array.isArray(value)) {
|
||||
return t.objectProperty(t.stringLiteral(key), t.arrayExpression(convertArrayToAstExpression(value)))
|
||||
}
|
||||
if (value == null) {
|
||||
return t.objectProperty(t.stringLiteral(key), t.nullLiteral())
|
||||
}
|
||||
if (typeof value === 'object') {
|
||||
return t.objectProperty(t.stringLiteral(key), t.objectExpression(convertObjectToAstExpression(value)))
|
||||
}
|
||||
})
|
||||
return objArr
|
||||
}
|
||||
|
||||
// 最低限度的转义: https://github.com/mathiasbynens/jsesc#minimal
|
||||
function generateMinimalEscapeCode (ast) {
|
||||
return generate(ast, {
|
||||
jsescOption: {
|
||||
minimal: true
|
||||
}
|
||||
}).code
|
||||
}
|
||||
|
||||
function convertArrayToAstExpression (arr) {
|
||||
return arr.map(value => {
|
||||
if (typeof value === 'string') {
|
||||
return t.stringLiteral(value)
|
||||
}
|
||||
if (typeof value === 'number') {
|
||||
return t.numericLiteral(value)
|
||||
}
|
||||
if (typeof value === 'boolean') {
|
||||
return t.booleanLiteral(value)
|
||||
}
|
||||
if (Array.isArray(value)) {
|
||||
return convertArrayToAstExpression(value)
|
||||
}
|
||||
if (value == null) {
|
||||
return t.nullLiteral()
|
||||
}
|
||||
if (typeof value === 'object') {
|
||||
return t.objectExpression(convertObjectToAstExpression(value))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function convertSourceStringToAstExpression (str, opts = {}) {
|
||||
return template(str, Object.assign({}, babylonConfig, opts))()
|
||||
}
|
||||
|
||||
const getObjKey = (node) => {
|
||||
if (t.isIdentifier(node)) {
|
||||
return node.name
|
||||
} else {
|
||||
return node.value
|
||||
}
|
||||
}
|
||||
|
||||
exports.obj = convertObjectToAstExpression
|
||||
exports.array = convertArrayToAstExpression
|
||||
exports.source = convertSourceStringToAstExpression
|
||||
exports.getObjKey = getObjKey
|
||||
exports.generateMinimalEscapeCode = generateMinimalEscapeCode
|
|
@ -0,0 +1,604 @@
|
|||
const path = require('path')
|
||||
const crypto = require('crypto')
|
||||
const os = require('os')
|
||||
const fs = require('fs-extra')
|
||||
const execSync = require('child_process').execSync
|
||||
const chalk = require('chalk')
|
||||
const _ = require('lodash')
|
||||
|
||||
const pocessTypeEnum = {
|
||||
CREATE: 'create',
|
||||
COMPILE: 'compile',
|
||||
CONVERT: 'convert',
|
||||
COPY: 'copy',
|
||||
GENERATE: 'generate',
|
||||
MODIFY: 'modify',
|
||||
ERROR: 'error',
|
||||
WARNING: 'warning',
|
||||
UNLINK: 'unlink',
|
||||
REFERENCE: 'reference'
|
||||
}
|
||||
|
||||
const processTypeMap = {
|
||||
[pocessTypeEnum.CREATE]: {
|
||||
name: '创建',
|
||||
color: 'cyan'
|
||||
},
|
||||
[pocessTypeEnum.COMPILE]: {
|
||||
name: '编译',
|
||||
color: 'green'
|
||||
},
|
||||
[pocessTypeEnum.CONVERT]: {
|
||||
name: '转换',
|
||||
color: chalk.rgb(255, 136, 0)
|
||||
},
|
||||
[pocessTypeEnum.COPY]: {
|
||||
name: '拷贝',
|
||||
color: 'magenta'
|
||||
},
|
||||
[pocessTypeEnum.GENERATE]: {
|
||||
name: '生成',
|
||||
color: 'blue'
|
||||
},
|
||||
[pocessTypeEnum.MODIFY]: {
|
||||
name: '修改',
|
||||
color: 'yellow'
|
||||
},
|
||||
[pocessTypeEnum.ERROR]: {
|
||||
name: '错误',
|
||||
color: 'red'
|
||||
},
|
||||
[pocessTypeEnum.WARNING]: {
|
||||
name: '警告',
|
||||
color: 'yellow'
|
||||
},
|
||||
[pocessTypeEnum.UNLINK]: {
|
||||
name: '删除',
|
||||
color: 'magenta'
|
||||
},
|
||||
[pocessTypeEnum.START]: {
|
||||
name: '启动',
|
||||
color: 'green'
|
||||
},
|
||||
[pocessTypeEnum.REFERENCE]: {
|
||||
name: '引用',
|
||||
color: 'blue'
|
||||
}
|
||||
}
|
||||
|
||||
exports.pocessTypeEnum = pocessTypeEnum
|
||||
|
||||
exports.CSS_EXT = ['.css', '.scss', '.sass', '.less', '.styl', '.wxss', '.acss']
|
||||
exports.SCSS_EXT = ['.scss']
|
||||
exports.JS_EXT = ['.js', '.jsx']
|
||||
exports.TS_EXT = ['.ts', '.tsx']
|
||||
exports.REG_JS = /\.js(\?.*)?$/
|
||||
exports.REG_SCRIPT = /\.(js|jsx)(\?.*)?$/
|
||||
exports.REG_TYPESCRIPT = /\.(tsx|ts)(\?.*)?$/
|
||||
exports.REG_SCRIPTS = /\.[tj]sx?$/i
|
||||
exports.REG_STYLE = /\.(css|scss|sass|less|styl|wxss)(\?.*)?$/
|
||||
exports.REG_MEDIA = /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/
|
||||
exports.REG_IMAGE = /\.(png|jpe?g|gif|bpm|svg|webp)(\?.*)?$/
|
||||
exports.REG_FONT = /\.(woff2?|eot|ttf|otf)(\?.*)?$/
|
||||
exports.REG_JSON = /\.json(\?.*)?$/
|
||||
exports.REG_WXML_IMPORT = /<import(.*)?src=(?:(?:'([^']*)')|(?:"([^"]*)"))/gi
|
||||
exports.REG_URL = /^(?:(?:(?:https?|ftp):)?\/\/)(?:\S+(?::\S*)?@)?(?:(?!(?:10|127)(?:\.\d{1,3}){3})(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)(?:\.(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)*(?:\.(?:[a-z\u00a1-\uffff]{2,}))\.?)(?::\d{2,5})?(?:[/?#]\S*)?$/i
|
||||
|
||||
exports.CSS_IMPORT_REG = /@import (["'])(.+?)\1;/g
|
||||
|
||||
exports.BUILD_TYPES = {
|
||||
WEAPP: 'weapp',
|
||||
H5: 'h5',
|
||||
RN: 'rn',
|
||||
SWAN: 'swan',
|
||||
ALIPAY: 'alipay',
|
||||
TT: 'tt',
|
||||
UI: 'ui'
|
||||
}
|
||||
|
||||
exports.MINI_APP_FILES = {
|
||||
[exports.BUILD_TYPES.WEAPP]: {
|
||||
TEMPL: '.wxml',
|
||||
STYLE: '.wxss',
|
||||
SCRIPT: '.js',
|
||||
CONFIG: '.json'
|
||||
},
|
||||
[exports.BUILD_TYPES.SWAN]: {
|
||||
TEMPL: '.swan',
|
||||
STYLE: '.css',
|
||||
SCRIPT: '.js',
|
||||
CONFIG: '.json'
|
||||
},
|
||||
[exports.BUILD_TYPES.ALIPAY]: {
|
||||
TEMPL: '.axml',
|
||||
STYLE: '.acss',
|
||||
SCRIPT: '.js',
|
||||
CONFIG: '.json'
|
||||
},
|
||||
[exports.BUILD_TYPES.TT]: {
|
||||
TEMPL: '.ttml',
|
||||
STYLE: '.ttss',
|
||||
SCRIPT: '.js',
|
||||
CONFIG: '.json'
|
||||
}
|
||||
}
|
||||
|
||||
exports.CONFIG_MAP = {
|
||||
[exports.BUILD_TYPES.WEAPP]: {
|
||||
navigationBarTitleText: 'navigationBarTitleText',
|
||||
navigationBarBackgroundColor: 'navigationBarBackgroundColor',
|
||||
enablePullDownRefresh: 'enablePullDownRefresh',
|
||||
list: 'list',
|
||||
text: 'text',
|
||||
iconPath: 'iconPath',
|
||||
selectedIconPath: 'selectedIconPath'
|
||||
},
|
||||
[exports.BUILD_TYPES.SWAN]: {
|
||||
navigationBarTitleText: 'navigationBarTitleText',
|
||||
navigationBarBackgroundColor: 'navigationBarBackgroundColor',
|
||||
enablePullDownRefresh: 'enablePullDownRefresh',
|
||||
list: 'list',
|
||||
text: 'text',
|
||||
iconPath: 'iconPath',
|
||||
selectedIconPath: 'selectedIconPath'
|
||||
},
|
||||
[exports.BUILD_TYPES.TT]: {
|
||||
navigationBarTitleText: 'navigationBarTitleText',
|
||||
navigationBarBackgroundColor: 'navigationBarBackgroundColor',
|
||||
enablePullDownRefresh: 'enablePullDownRefresh',
|
||||
list: 'list',
|
||||
text: 'text',
|
||||
iconPath: 'iconPath',
|
||||
selectedIconPath: 'selectedIconPath'
|
||||
},
|
||||
[exports.BUILD_TYPES.ALIPAY]: {
|
||||
navigationBarTitleText: 'defaultTitle',
|
||||
navigationBarBackgroundColor: 'titleBarColor',
|
||||
enablePullDownRefresh: 'pullRefresh',
|
||||
list: 'items',
|
||||
text: 'name',
|
||||
iconPath: 'icon',
|
||||
selectedIconPath: 'activeIcon'
|
||||
}
|
||||
}
|
||||
|
||||
exports.PROJECT_CONFIG = 'config/index.js'
|
||||
//@fix
|
||||
exports.PROJECT_CONFIG_H5 = 'config/h5.js'
|
||||
|
||||
exports.DEVICE_RATIO = {
|
||||
'640': 2.34 / 2,
|
||||
'750': 1,
|
||||
'828': 1.81 / 2
|
||||
}
|
||||
|
||||
exports.FILE_PROCESSOR_MAP = {
|
||||
'.js': 'babel',
|
||||
'.scss': 'sass',
|
||||
'.sass': 'sass',
|
||||
'.less': 'less',
|
||||
'.styl': 'stylus'
|
||||
}
|
||||
|
||||
exports.isNpmPkg = function (name) {
|
||||
if (/^(\.|\/)/.test(name)) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
exports.isAliasPath = function (name, pathAlias = {}) {
|
||||
const prefixs = Object.keys(pathAlias)
|
||||
if (prefixs.length === 0) {
|
||||
return false
|
||||
}
|
||||
return prefixs.includes(name) || (new RegExp(`^(${prefixs.join('|')})/`).test(name))
|
||||
}
|
||||
|
||||
exports.replaceAliasPath = function (filePath, name, pathAlias = {}) {
|
||||
// 后续的 path.join 在遇到符号链接时将会解析为真实路径,如果
|
||||
// 这里的 filePath 没有做同样的处理,可能会导致 import 指向
|
||||
// 源代码文件,导致文件被意外修改
|
||||
filePath = fs.realpathSync(filePath)
|
||||
|
||||
const prefixs = Object.keys(pathAlias)
|
||||
if (prefixs.includes(name)) {
|
||||
return exports.promoteRelativePath(path.relative(filePath, fs.realpathSync(pathAlias[name])))
|
||||
}
|
||||
const reg = new RegExp(`^(${prefixs.join('|')})/(.*)`)
|
||||
name = name.replace(reg, function (m, $1, $2) {
|
||||
return exports.promoteRelativePath(path.relative(filePath, path.join(pathAlias[$1], $2)))
|
||||
})
|
||||
return name
|
||||
}
|
||||
|
||||
exports.promoteRelativePath = function (fPath) {
|
||||
const fPathArr = fPath.split(path.sep)
|
||||
let dotCount = 0
|
||||
fPathArr.forEach(item => {
|
||||
if (item.indexOf('..') >= 0) {
|
||||
dotCount++
|
||||
}
|
||||
})
|
||||
if (dotCount === 1) {
|
||||
fPathArr.splice(0, 1, '.')
|
||||
return fPathArr.join('/')
|
||||
}
|
||||
if (dotCount > 1) {
|
||||
fPathArr.splice(0, 1)
|
||||
return fPathArr.join('/')
|
||||
}
|
||||
return fPath.replace(/\\/g, '/')
|
||||
}
|
||||
|
||||
exports.replaceAsync = async function (str, regex, asyncFn) {
|
||||
const promises = []
|
||||
str.replace(regex, (match, ...args) => {
|
||||
const promise = asyncFn(match, ...args)
|
||||
promises.push(promise)
|
||||
})
|
||||
const data = await Promise.all(promises)
|
||||
return str.replace(regex, () => data.shift())
|
||||
}
|
||||
|
||||
exports.homedir = (function () {
|
||||
let homedir = null
|
||||
const env = process.env
|
||||
const home = env.HOME
|
||||
const user = env.LOGNAME || env.USER || env.LNAME || env.USERNAME
|
||||
if (process.platform === 'win32') {
|
||||
homedir = env.USERPROFILE || env.HOMEDRIVE + env.HOMEPATH || home || null
|
||||
} else if (process.platform === 'darwin') {
|
||||
homedir = home || (user ? `/Users/${user}` : null)
|
||||
} else if (process.platform === 'linux') {
|
||||
homedir = home || (process.getuid() === 0 ? '/root' : (user ? `/home/${user}` : null))
|
||||
}
|
||||
return typeof os.homedir === 'function' ? os.homedir : function () {
|
||||
return homedir
|
||||
}
|
||||
})()
|
||||
|
||||
exports.getRootPath = function () {
|
||||
return path.resolve(__dirname, '../../')
|
||||
}
|
||||
|
||||
exports.getTaroPath = function () {
|
||||
const taroPath = path.join(exports.homedir(), '.taro')
|
||||
if (!fs.existsSync(taroPath)) {
|
||||
fs.mkdirSync(taroPath)
|
||||
}
|
||||
return taroPath
|
||||
}
|
||||
|
||||
exports.setConfig = function (config) {
|
||||
const taroPath = exports.getTaroPath()
|
||||
if (typeof config === 'object') {
|
||||
const oldConfig = exports.getConfig()
|
||||
config = Object.assign({}, oldConfig, config)
|
||||
fs.writeFileSync(path.join(taroPath, 'config.json'), JSON.stringify(config, null, 2))
|
||||
}
|
||||
}
|
||||
|
||||
exports.getConfig = function () {
|
||||
const configPath = path.join(exports.getTaroPath(), 'config.json')
|
||||
if (fs.existsSync(configPath)) {
|
||||
return require(configPath)
|
||||
}
|
||||
return {}
|
||||
}
|
||||
|
||||
exports.getSystemUsername = function () {
|
||||
const userHome = exports.homedir()
|
||||
const systemUsername = process.env.USER || path.basename(userHome)
|
||||
return systemUsername
|
||||
}
|
||||
|
||||
exports.getPkgVersion = function () {
|
||||
return require(path.join(exports.getRootPath(), 'package.json')).version
|
||||
}
|
||||
|
||||
exports.getPkgItemByKey = function (key) {
|
||||
const packageMap = require(path.join(exports.getRootPath(), 'package.json'))
|
||||
if (Object.keys(packageMap).indexOf(key) === -1) {
|
||||
return {}
|
||||
} else {
|
||||
return packageMap[key]
|
||||
}
|
||||
}
|
||||
|
||||
exports.printPkgVersion = function () {
|
||||
const taroVersion = exports.getPkgVersion()
|
||||
console.log(`👽 Taro-Omi v${taroVersion}`)
|
||||
console.log()
|
||||
}
|
||||
|
||||
exports.shouldUseYarn = function () {
|
||||
try {
|
||||
execSync('yarn --version', { stdio: 'ignore' })
|
||||
return true
|
||||
} catch (e) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
exports.shouldUseCnpm = function () {
|
||||
try {
|
||||
execSync('cnpm --version', { stdio: 'ignore' })
|
||||
return true
|
||||
} catch (e) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
exports.isPublic = function isPublic (addr) {
|
||||
return !exports.isPrivate(addr)
|
||||
}
|
||||
|
||||
exports.isEmptyObject = function (obj) {
|
||||
if (obj == null) {
|
||||
return true
|
||||
}
|
||||
for (const key in obj) {
|
||||
if (obj.hasOwnProperty(key)) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
exports.urlJoin = function () {
|
||||
function normalize (str) {
|
||||
return str
|
||||
.replace(/([/]+)/g, '/')
|
||||
.replace(/\/\?(?!\?)/g, '?')
|
||||
.replace(/\/#/g, '#')
|
||||
.replace(/:\//g, '://')
|
||||
}
|
||||
|
||||
const joined = [].slice.call(arguments, 0).join('/')
|
||||
return normalize(joined)
|
||||
}
|
||||
|
||||
exports.resolveScriptPath = function (p) {
|
||||
let realPath = p
|
||||
const SCRIPT_EXT = exports.JS_EXT.concat(exports.TS_EXT)
|
||||
for (let i = 0; i < SCRIPT_EXT.length; i++) {
|
||||
const item = SCRIPT_EXT[i]
|
||||
if (fs.existsSync(`${p}${item}`)) {
|
||||
return `${p}${item}`
|
||||
}
|
||||
if (fs.existsSync(`${p}${path.sep}index${item}`)) {
|
||||
return `${p}${path.sep}index${item}`
|
||||
}
|
||||
}
|
||||
return realPath
|
||||
}
|
||||
|
||||
exports.resolveStylePath = function (p) {
|
||||
let realPath = p
|
||||
const CSS_EXT = exports.CSS_EXT
|
||||
for (let i = 0; i < CSS_EXT.length; i++) {
|
||||
const item = CSS_EXT[i]
|
||||
if (fs.existsSync(`${p}${item}`)) {
|
||||
return `${p}${item}`
|
||||
}
|
||||
}
|
||||
return realPath
|
||||
}
|
||||
|
||||
exports.isDifferentArray = function (a, b) {
|
||||
if (!Array.isArray(a) || !Array.isArray(b)) {
|
||||
return true
|
||||
}
|
||||
if (a.length !== b.length) {
|
||||
return true
|
||||
}
|
||||
a = a.sort()
|
||||
b = b.sort()
|
||||
for (let i = 0; i < a.length; i++) {
|
||||
if (a[i] !== b[i]) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
exports.checksum = function (buf, length) {
|
||||
if (!Buffer.isBuffer(buf)) {
|
||||
buf = Buffer.from(buf)
|
||||
}
|
||||
return crypto.createHash('md5').update(buf).digest('hex').slice(0, length || 8)
|
||||
}
|
||||
|
||||
exports.printLog = function (type, tag, filePath) {
|
||||
const typeShow = processTypeMap[type]
|
||||
const tagLen = tag.replace(/[\u0391-\uFFE5]/g, 'aa').length
|
||||
const tagFormatLen = 8
|
||||
if (tagLen < tagFormatLen) {
|
||||
const rightPadding = new Array(tagFormatLen - tagLen + 1).join(' ')
|
||||
tag += rightPadding
|
||||
}
|
||||
const padding = ''
|
||||
filePath = filePath || ''
|
||||
if (typeof typeShow.color === 'string') {
|
||||
console.log(chalk[typeShow.color](typeShow.name), padding, tag, padding, filePath)
|
||||
} else {
|
||||
console.log(typeShow.color(typeShow.name), padding, tag, padding, filePath)
|
||||
}
|
||||
}
|
||||
|
||||
exports.replaceContentEnv = function (content, env) {
|
||||
if (env && !exports.isEmptyObject(env)) {
|
||||
for (const key in env) {
|
||||
const reg = new RegExp(`process.env.${key}`, 'g')
|
||||
content = content.replace(reg, env[key])
|
||||
}
|
||||
return content
|
||||
}
|
||||
return content
|
||||
}
|
||||
|
||||
exports.generateEnvList = function (env) {
|
||||
const res = { }
|
||||
if (env && !exports.isEmptyObject(env)) {
|
||||
for (const key in env) {
|
||||
try {
|
||||
res[`process.env.${key}`] = JSON.parse(env[key])
|
||||
} catch (err) {
|
||||
res[`process.env.${key}`] = env[key]
|
||||
}
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
exports.replaceContentConstants = function (content, constants) {
|
||||
if (constants && !exports.isEmptyObject(constants)) {
|
||||
for (const key in constants) {
|
||||
const reg = new RegExp(key, 'g')
|
||||
content = content.replace(reg, constants[key])
|
||||
}
|
||||
return content
|
||||
}
|
||||
return content
|
||||
}
|
||||
|
||||
exports.generateConstantsList = function (constants) {
|
||||
const res = { }
|
||||
if (constants && !exports.isEmptyObject(constants)) {
|
||||
for (const key in constants) {
|
||||
try {
|
||||
res[key] = JSON.parse(constants[key])
|
||||
} catch (err) {
|
||||
res[key] = constants[key]
|
||||
}
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
exports.cssImports = function (content) {
|
||||
let match = {}
|
||||
const results = []
|
||||
content = String(content).replace(/\/\*.+?\*\/|\/\/.*(?=[\n\r])/g, '')
|
||||
while ((match = exports.CSS_IMPORT_REG.exec(content))) {
|
||||
results.push(match[2])
|
||||
}
|
||||
return results
|
||||
}
|
||||
|
||||
exports.processStyleImports = function (content, adapter, process) {
|
||||
const style = []
|
||||
const imports = []
|
||||
const styleReg = new RegExp(`\\${exports.MINI_APP_FILES[adapter].STYLE}`)
|
||||
content = content.replace(exports.CSS_IMPORT_REG, (m, $1, $2) => {
|
||||
if (styleReg.test($2)) {
|
||||
style.push(m)
|
||||
imports.push($2)
|
||||
if (process && typeof process === 'function') {
|
||||
return process(m, $2)
|
||||
}
|
||||
return ''
|
||||
}
|
||||
if (process && typeof process === 'function') {
|
||||
return process(m, $2)
|
||||
}
|
||||
return m
|
||||
})
|
||||
return {
|
||||
content,
|
||||
style,
|
||||
imports
|
||||
}
|
||||
}
|
||||
/*eslint-disable*/
|
||||
const retries = (process.platform === 'win32') ? 100 : 1
|
||||
exports.emptyDirectory = function (dirPath, opts = { excludes: [] }) {
|
||||
if (fs.existsSync(dirPath)) {
|
||||
fs.readdirSync(dirPath).forEach(file => {
|
||||
const curPath = path.join(dirPath, file)
|
||||
if (fs.lstatSync(curPath).isDirectory()) {
|
||||
let removed = false
|
||||
let i = 0 // retry counter
|
||||
|
||||
do {
|
||||
try {
|
||||
if (!opts.excludes.length || !opts.excludes.some(item => curPath.indexOf(item) >= 0)) {
|
||||
exports.emptyDirectory(curPath)
|
||||
fs.rmdirSync(curPath)
|
||||
}
|
||||
removed = true
|
||||
} catch (e) {
|
||||
} finally {
|
||||
if (++i < retries) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
} while (!removed)
|
||||
} else {
|
||||
fs.unlinkSync(curPath)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
/* eslint-enable */
|
||||
|
||||
exports.recursiveFindNodeModules = function (filePath) {
|
||||
const dirname = path.dirname(filePath)
|
||||
const nodeModules = path.join(dirname, 'node_modules')
|
||||
if (fs.existsSync(nodeModules)) {
|
||||
return nodeModules
|
||||
}
|
||||
return exports.recursiveFindNodeModules(dirname)
|
||||
}
|
||||
|
||||
exports.UPDATE_PACKAGE_LIST = [
|
||||
'@tarojs/taro',
|
||||
'@tarojs/async-await',
|
||||
'@tarojs/cli',
|
||||
'@tarojs/components',
|
||||
'@tarojs/components-rn',
|
||||
'@tarojs/taro-h5',
|
||||
'@tarojs/taro-swan',
|
||||
'@tarojs/taro-alipay',
|
||||
'@tarojs/taro-tt',
|
||||
'@tarojs/plugin-babel',
|
||||
'@tarojs/plugin-csso',
|
||||
'@tarojs/plugin-sass',
|
||||
'@tarojs/plugin-less',
|
||||
'@tarojs/plugin-stylus',
|
||||
'@tarojs/plugin-uglifyjs',
|
||||
'@tarojs/redux',
|
||||
'@tarojs/redux-h5',
|
||||
'@tarojs/taro-redux-rn',
|
||||
'@tarojs/taro-router-rn',
|
||||
'@tarojs/taro-rn',
|
||||
'@tarojs/rn-runner',
|
||||
'@tarojs/router',
|
||||
'@tarojs/taro-weapp',
|
||||
'@tarojs/webpack-runner',
|
||||
'postcss-plugin-constparse',
|
||||
'eslint-config-taro',
|
||||
'eslint-plugin-taro',
|
||||
'taro-transformer-wx',
|
||||
'postcss-pxtransform',
|
||||
'babel-plugin-transform-jsx-to-stylesheet',
|
||||
'@tarojs/mobx',
|
||||
'@tarojs/mobx-h5',
|
||||
'@tarojs/mobx-rn',
|
||||
'@tarojs/mobx-common',
|
||||
'@tarojs/mobx-prop-types'
|
||||
]
|
||||
|
||||
exports.pascalCase = (str) => str.charAt(0).toUpperCase() + _.camelCase(str.substr(1))
|
||||
|
||||
exports.getInstalledNpmPkgVersion = function (pkgName, basedir) {
|
||||
const resolvePath = require('resolve')
|
||||
try {
|
||||
const pkg = resolvePath.sync(`${pkgName}/package.json`, { basedir })
|
||||
const pkgJson = fs.readJSONSync(pkg)
|
||||
return pkgJson.version
|
||||
} catch (err) {
|
||||
return null
|
||||
}
|
||||
}
|
|
@ -0,0 +1,164 @@
|
|||
const resolvePath = require('resolve')
|
||||
const spawn = require('cross-spawn')
|
||||
const chalk = require('chalk')
|
||||
|
||||
const Util = require('./')
|
||||
|
||||
const basedir = process.cwd()
|
||||
const taroPluginPrefix = '@tarojs/plugin-'
|
||||
const PEERS = /UNMET PEER DEPENDENCY ([a-z\-0-9.]+)@(.+)/gm
|
||||
const npmCached = {}
|
||||
const erroneous = []
|
||||
const defaultInstallOptions = {
|
||||
dev: false,
|
||||
peerDependencies: true
|
||||
}
|
||||
|
||||
function resolveNpm (pluginName) {
|
||||
if (!npmCached[pluginName]) {
|
||||
return new Promise((resolve, reject) => {
|
||||
resolvePath(`${pluginName}`, {basedir}, (err, res) => {
|
||||
if (err) {
|
||||
return reject(err)
|
||||
}
|
||||
npmCached[pluginName] = res
|
||||
resolve(res)
|
||||
})
|
||||
})
|
||||
}
|
||||
return Promise.resolve(npmCached[pluginName])
|
||||
}
|
||||
|
||||
function resolveNpmSync (pluginName) {
|
||||
try {
|
||||
if (!npmCached[pluginName]) {
|
||||
const res = resolvePath.sync(pluginName, {basedir})
|
||||
return res
|
||||
}
|
||||
return npmCached[pluginName]
|
||||
} catch (err) {
|
||||
if (err.code === 'MODULE_NOT_FOUND') {
|
||||
console.log(chalk.cyan(`缺少npm包${pluginName},开始安装...`))
|
||||
const installOptions = {}
|
||||
if (pluginName.indexOf(taroPluginPrefix) >= 0) {
|
||||
installOptions.dev = true
|
||||
}
|
||||
installNpmPkg(pluginName, installOptions)
|
||||
return resolveNpmSync(pluginName)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function installNpmPkg (pkgList, options) {
|
||||
if (!pkgList) {
|
||||
return
|
||||
}
|
||||
if (!Array.isArray(pkgList)) {
|
||||
pkgList = [pkgList]
|
||||
}
|
||||
pkgList = pkgList.filter(dep => {
|
||||
return erroneous.indexOf(dep) === -1
|
||||
})
|
||||
|
||||
if (!pkgList.length) {
|
||||
return
|
||||
}
|
||||
options = Object.assign({}, defaultInstallOptions, options)
|
||||
let installer = ''
|
||||
let args = []
|
||||
|
||||
if (Util.shouldUseYarn()) {
|
||||
installer = 'yarn'
|
||||
} else if (Util.shouldUseCnpm()) {
|
||||
installer = 'cnpm'
|
||||
} else {
|
||||
installer = 'npm'
|
||||
}
|
||||
|
||||
if (Util.shouldUseYarn()) {
|
||||
args = ['add'].concat(pkgList).filter(Boolean)
|
||||
args.push('--silent', '--no-progress')
|
||||
if (options.dev) {
|
||||
args.push('-D')
|
||||
}
|
||||
} else {
|
||||
args = ['install'].concat(pkgList).filter(Boolean)
|
||||
args.push('--silent', '--no-progress')
|
||||
if (options.dev) {
|
||||
args.push('--save-dev')
|
||||
} else {
|
||||
args.push('--save')
|
||||
}
|
||||
}
|
||||
const output = spawn.sync(installer, args, {
|
||||
stdio: ['ignore', 'pipe', 'inherit']
|
||||
})
|
||||
if (output.status) {
|
||||
pkgList.forEach(dep => {
|
||||
erroneous.push(dep)
|
||||
})
|
||||
}
|
||||
let matches = null
|
||||
const peers = []
|
||||
|
||||
while ((matches = PEERS.exec(output.stdout))) {
|
||||
const pkg = matches[1]
|
||||
const version = matches[2]
|
||||
if (version.match(' ')) {
|
||||
peers.push(pkg)
|
||||
} else {
|
||||
peers.push(`${pkg}@${version}`)
|
||||
}
|
||||
}
|
||||
if (options.peerDependencies && peers.length) {
|
||||
console.info('正在安装 peerDependencies...')
|
||||
installNpmPkg(peers, options)
|
||||
}
|
||||
return output
|
||||
}
|
||||
|
||||
async function callPlugin (pluginName, content, file, config) {
|
||||
const pluginFn = await getNpmPkg(`${taroPluginPrefix}${pluginName}`)
|
||||
return pluginFn(content, file, config)
|
||||
}
|
||||
|
||||
function callPluginSync (pluginName, content, file, config) {
|
||||
const pluginFn = getNpmPkgSync(`${taroPluginPrefix}${pluginName}`)
|
||||
return pluginFn(content, file, config)
|
||||
}
|
||||
|
||||
function getNpmPkgSync (npmName) {
|
||||
const npmPath = resolveNpmSync(npmName)
|
||||
const npmFn = require(npmPath)
|
||||
return npmFn
|
||||
}
|
||||
|
||||
async function getNpmPkg (npmName) {
|
||||
let npmPath
|
||||
try {
|
||||
npmPath = resolveNpmSync(npmName)
|
||||
} catch (err) {
|
||||
if (err.code === 'MODULE_NOT_FOUND') {
|
||||
console.log(chalk.cyan(`缺少npm包${npmName},开始安装...`))
|
||||
const installOptions = {}
|
||||
if (npmName.indexOf(taroPluginPrefix) >= 0) {
|
||||
installOptions.dev = true
|
||||
}
|
||||
installNpmPkg(npmName, installOptions)
|
||||
npmPath = await resolveNpm(npmName)
|
||||
}
|
||||
}
|
||||
const npmFn = require(npmPath)
|
||||
return npmFn
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
taroPluginPrefix,
|
||||
installNpmPkg,
|
||||
resolveNpm,
|
||||
resolveNpmSync,
|
||||
callPlugin,
|
||||
callPluginSync,
|
||||
getNpmPkg,
|
||||
getNpmPkgSync
|
||||
}
|
|
@ -0,0 +1,270 @@
|
|||
const fs = require('fs-extra')
|
||||
const path = require('path')
|
||||
const resolvePath = require('resolve')
|
||||
const wxTransformer = require('../../../taro-transformer-wx/lib/src/index.js').default
|
||||
const babel = require('babel-core')
|
||||
const traverse = require('babel-traverse').default
|
||||
const t = require('babel-types')
|
||||
const generate = require('babel-generator').default
|
||||
const _ = require('lodash')
|
||||
|
||||
const defaultUglifyConfig = require('../config/uglify')
|
||||
|
||||
const {
|
||||
isNpmPkg,
|
||||
promoteRelativePath,
|
||||
printLog,
|
||||
pocessTypeEnum,
|
||||
PROJECT_CONFIG,
|
||||
generateEnvList,
|
||||
REG_TYPESCRIPT,
|
||||
BUILD_TYPES,
|
||||
REG_STYLE,
|
||||
recursiveFindNodeModules
|
||||
} = require('./index')
|
||||
|
||||
const CONFIG = require('../config')
|
||||
const defaultBabelConfig = require('../config/babel')
|
||||
|
||||
const npmProcess = require('./npm')
|
||||
|
||||
const excludeNpmPkgs = ['ReactPropTypes']
|
||||
|
||||
const resolvedCache = {}
|
||||
const copyedFiles = {}
|
||||
|
||||
const basedir = process.cwd()
|
||||
const configDir = path.join(basedir, PROJECT_CONFIG)
|
||||
const projectConfig = require(configDir)(_.merge)
|
||||
const pluginsConfig = projectConfig.plugins || {}
|
||||
const outputDirName = projectConfig.outputRoot || CONFIG.OUTPUT_DIR
|
||||
|
||||
const babelConfig = _.mergeWith({}, defaultBabelConfig, pluginsConfig.babel, (objValue, srcValue) => {
|
||||
if (Array.isArray(objValue)) {
|
||||
return Array.from(new Set(srcValue.concat(objValue)))
|
||||
}
|
||||
})
|
||||
|
||||
function resolveNpmPkgMainPath (pkgName, isProduction, npmConfig, buildAdapter = BUILD_TYPES.WEAPP, root = basedir) {
|
||||
try {
|
||||
return resolvePath.sync(pkgName, { basedir: root })
|
||||
} catch (err) {
|
||||
if (err.code === 'MODULE_NOT_FOUND') {
|
||||
console.log(`缺少npm包${pkgName},开始安装...`)
|
||||
const installOptions = {}
|
||||
if (pkgName.indexOf(npmProcess.taroPluginPrefix) >= 0) {
|
||||
installOptions.dev = true
|
||||
}
|
||||
npmProcess.installNpmPkg(pkgName, installOptions)
|
||||
return resolveNpmPkgMainPath(pkgName, isProduction, npmConfig, buildAdapter, root)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function resolveNpmFilesPath (pkgName, isProduction, npmConfig, buildAdapter = BUILD_TYPES.WEAPP, root = basedir, compileInclude = []) {
|
||||
if (!resolvedCache[pkgName]) {
|
||||
const res = resolveNpmPkgMainPath(pkgName, isProduction, npmConfig, buildAdapter, root)
|
||||
resolvedCache[pkgName] = {
|
||||
main: res,
|
||||
files: []
|
||||
}
|
||||
resolvedCache[pkgName].files.push(res)
|
||||
recursiveRequire(res, resolvedCache[pkgName].files, isProduction, npmConfig, buildAdapter, compileInclude)
|
||||
}
|
||||
return resolvedCache[pkgName]
|
||||
}
|
||||
|
||||
function parseAst (ast, filePath, files, isProduction, npmConfig, buildAdapter = BUILD_TYPES.WEAPP, compileInclude) {
|
||||
const excludeRequire = []
|
||||
traverse(ast, {
|
||||
IfStatement (astPath) {
|
||||
astPath.traverse({
|
||||
BinaryExpression (astPath) {
|
||||
const node = astPath.node
|
||||
const left = node.left
|
||||
if (generate(left).code === 'process.env.TARO_ENV' &&
|
||||
node.right.value !== buildAdapter) {
|
||||
const consequentSibling = astPath.getSibling('consequent')
|
||||
consequentSibling.traverse({
|
||||
CallExpression (astPath) {
|
||||
if (astPath.get('callee').isIdentifier({ name: 'require' })) {
|
||||
const arg = astPath.get('arguments')[0]
|
||||
if (t.isStringLiteral(arg.node)) {
|
||||
excludeRequire.push(arg.node.value)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
Program: {
|
||||
exit (astPath) {
|
||||
astPath.traverse({
|
||||
CallExpression (astPath) {
|
||||
const node = astPath.node
|
||||
const callee = node.callee
|
||||
if (callee.name === 'require') {
|
||||
const args = node.arguments
|
||||
let requirePath = args[0].value
|
||||
if (excludeRequire.indexOf(requirePath) < 0) {
|
||||
if (isNpmPkg(requirePath)) {
|
||||
if (excludeNpmPkgs.indexOf(requirePath) < 0) {
|
||||
const res = resolveNpmFilesPath(requirePath, isProduction, npmConfig, buildAdapter, path.dirname(recursiveFindNodeModules(filePath)), compileInclude)
|
||||
let relativeRequirePath = promoteRelativePath(path.relative(filePath, res.main))
|
||||
relativeRequirePath = relativeRequirePath.replace(/node_modules/g, npmConfig.name)
|
||||
if (buildAdapter === BUILD_TYPES.ALIPAY) {
|
||||
relativeRequirePath = relativeRequirePath.replace(/@/g, '_')
|
||||
}
|
||||
args[0].value = relativeRequirePath
|
||||
}
|
||||
} else {
|
||||
let realRequirePath = path.resolve(path.dirname(filePath), requirePath)
|
||||
let tempPathWithJS = `${realRequirePath}.js`
|
||||
let tempPathWithIndexJS = `${realRequirePath}${path.sep}index.js`
|
||||
if (fs.existsSync(tempPathWithJS)) {
|
||||
realRequirePath = tempPathWithJS
|
||||
requirePath += '.js'
|
||||
} else if (fs.existsSync(tempPathWithIndexJS)) {
|
||||
realRequirePath = tempPathWithIndexJS
|
||||
requirePath += '/index.js'
|
||||
}
|
||||
if (files.indexOf(realRequirePath) < 0) {
|
||||
files.push(realRequirePath)
|
||||
recursiveRequire(realRequirePath, files, isProduction, npmConfig, buildAdapter, compileInclude)
|
||||
}
|
||||
args[0].value = requirePath
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
return generate(ast).code
|
||||
}
|
||||
|
||||
async function recursiveRequire (filePath, files, isProduction, npmConfig = {}, buildAdapter, compileInclude = []) {
|
||||
let fileContent = fs.readFileSync(filePath).toString()
|
||||
let outputNpmPath
|
||||
if (!npmConfig.dir) {
|
||||
const cwdRelate2Npm = path.relative(
|
||||
filePath.slice(0, filePath.search('node_modules')),
|
||||
process.cwd()
|
||||
)
|
||||
outputNpmPath = filePath.replace('node_modules', path.join(cwdRelate2Npm, outputDirName, npmConfig.name))
|
||||
outputNpmPath = outputNpmPath.replace(/node_modules/g, npmConfig.name)
|
||||
} else {
|
||||
let npmFilePath = filePath.match(/(?=(node_modules)).*/)[0]
|
||||
npmFilePath = npmFilePath.replace(/node_modules/g, npmConfig.name)
|
||||
outputNpmPath = path.join(path.resolve(configDir, '..', npmConfig.dir), npmFilePath)
|
||||
}
|
||||
if (buildAdapter === BUILD_TYPES.ALIPAY) {
|
||||
outputNpmPath = outputNpmPath.replace(/@/g, '_')
|
||||
}
|
||||
if (REG_STYLE.test(path.basename(filePath))) {
|
||||
return
|
||||
}
|
||||
fileContent = npmCodeHack(filePath, fileContent, buildAdapter)
|
||||
try {
|
||||
const constantsReplaceList = Object.assign({
|
||||
'process.env.TARO_ENV': buildAdapter
|
||||
}, generateEnvList(projectConfig.env || {}))
|
||||
const transformResult = wxTransformer({
|
||||
code: fileContent,
|
||||
sourcePath: filePath,
|
||||
outputPath: outputNpmPath,
|
||||
isNormal: true,
|
||||
adapter: buildAdapter,
|
||||
isTyped: REG_TYPESCRIPT.test(filePath),
|
||||
env: constantsReplaceList
|
||||
})
|
||||
const ast = babel.transformFromAst(transformResult.ast, '', {
|
||||
plugins: [
|
||||
[require('babel-plugin-transform-define').default, constantsReplaceList]
|
||||
]
|
||||
}).ast
|
||||
fileContent = parseAst(ast, filePath, files, isProduction, npmConfig, buildAdapter, compileInclude)
|
||||
} catch (err) {
|
||||
console.log(err)
|
||||
}
|
||||
if (!copyedFiles[outputNpmPath]) {
|
||||
if (compileInclude && compileInclude.length) {
|
||||
const filePathArr = filePath.split(path.sep)
|
||||
const nodeModulesIndex = filePathArr.indexOf('node_modules')
|
||||
const npmPkgName = filePathArr[nodeModulesIndex + 1]
|
||||
if (compileInclude.indexOf(npmPkgName) >= 0) {
|
||||
const compileScriptRes = await npmProcess.callPlugin('babel', fileContent, filePath, babelConfig)
|
||||
fileContent = compileScriptRes.code
|
||||
}
|
||||
}
|
||||
if (isProduction) {
|
||||
const uglifyPluginConfig = pluginsConfig.uglify || { enable: true }
|
||||
if (uglifyPluginConfig.enable) {
|
||||
const uglifyConfig = Object.assign(defaultUglifyConfig, uglifyPluginConfig.config || {})
|
||||
const uglifyResult = npmProcess.callPluginSync('uglifyjs', fileContent, outputNpmPath, uglifyConfig)
|
||||
if (uglifyResult.error) {
|
||||
printLog(pocessTypeEnum.ERROR, '压缩错误', `文件${filePath}`)
|
||||
console.log(uglifyResult.error)
|
||||
} else {
|
||||
fileContent = uglifyResult.code
|
||||
}
|
||||
}
|
||||
}
|
||||
fs.ensureDirSync(path.dirname(outputNpmPath))
|
||||
fs.writeFileSync(outputNpmPath, fileContent)
|
||||
let modifyOutput = outputNpmPath.replace(basedir + path.sep, '')
|
||||
modifyOutput = modifyOutput.split(path.sep).join('/')
|
||||
printLog(pocessTypeEnum.COPY, 'NPM文件', modifyOutput)
|
||||
copyedFiles[outputNpmPath] = true
|
||||
}
|
||||
}
|
||||
|
||||
function npmCodeHack (filePath, content, buildAdapter) {
|
||||
const basename = path.basename(filePath)
|
||||
switch (basename) {
|
||||
case 'lodash.js':
|
||||
case '_global.js':
|
||||
case 'lodash.min.js':
|
||||
if (buildAdapter === BUILD_TYPES.ALIPAY || buildAdapter === BUILD_TYPES.SWAN) {
|
||||
content = content.replace(/Function\(['"]return this['"]\)\(\)/, '{}')
|
||||
} else {
|
||||
content = content.replace(/Function\(['"]return this['"]\)\(\)/, 'this')
|
||||
}
|
||||
break
|
||||
case 'mobx.js':
|
||||
// 解决支付宝小程序全局window或global不存在的问题
|
||||
content = content.replace(
|
||||
/typeof window\s{0,}!==\s{0,}['"]undefined['"]\s{0,}\?\s{0,}window\s{0,}:\s{0,}global/,
|
||||
'typeof window !== "undefined" ? window : typeof global !== "undefined" ? global : {}'
|
||||
)
|
||||
break
|
||||
case '_html.js':
|
||||
content = 'module.exports = false;'
|
||||
break
|
||||
case '_microtask.js':
|
||||
content = content.replace('if(Observer)', 'if(false && Observer)')
|
||||
// IOS 1.10.2 Promise BUG
|
||||
content = content.replace('Promise && Promise.resolve', 'false && Promise && Promise.resolve')
|
||||
break
|
||||
case '_freeGlobal.js':
|
||||
content = content.replace('module.exports = freeGlobal;', 'module.exports = freeGlobal || this || global || {};')
|
||||
break
|
||||
}
|
||||
if (buildAdapter === BUILD_TYPES.ALIPAY && content.replace(/\s\r\n/g, '').length <= 0) {
|
||||
content = '// Empty file'
|
||||
}
|
||||
return content
|
||||
}
|
||||
|
||||
function getResolvedCache () {
|
||||
return resolvedCache
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
getResolvedCache,
|
||||
resolveNpmFilesPath,
|
||||
resolveNpmPkgMainPath
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,69 @@
|
|||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
var Adapters;
|
||||
(function (Adapters) {
|
||||
Adapters["weapp"] = "weapp";
|
||||
Adapters["swan"] = "swan";
|
||||
Adapters["alipay"] = "alipay";
|
||||
Adapters["quickapp"] = "quickapp";
|
||||
Adapters["tt"] = "tt";
|
||||
})(Adapters = exports.Adapters || (exports.Adapters = {}));
|
||||
const weixinAdapter = {
|
||||
if: 'wx:if',
|
||||
else: 'wx:else',
|
||||
elseif: 'wx:elif',
|
||||
for: 'wx:for',
|
||||
forItem: 'wx:for-item',
|
||||
forIndex: 'wx:for-index',
|
||||
key: 'wx:key',
|
||||
type: "weapp" /* weapp */
|
||||
};
|
||||
const swanAdapter = {
|
||||
if: 's-if',
|
||||
else: 's-else',
|
||||
elseif: 's-elif',
|
||||
for: 's-for',
|
||||
forItem: 's-for-item',
|
||||
forIndex: 's-for-index',
|
||||
key: 's-key',
|
||||
type: "swan" /* swan */
|
||||
};
|
||||
const alipayAdapter = {
|
||||
if: 'a:if',
|
||||
else: 'a:else',
|
||||
elseif: 'a:elif',
|
||||
for: 'a:for',
|
||||
forItem: 'a:for-item',
|
||||
forIndex: 'a:for-index',
|
||||
key: 'a:key',
|
||||
type: "alipay" /* alipay */
|
||||
};
|
||||
const ttAdapter = {
|
||||
if: 'tt:if',
|
||||
else: 'tt:else',
|
||||
elseif: 'tt:elif',
|
||||
for: 'tt:for',
|
||||
forItem: 'tt:for-item',
|
||||
forIndex: 'tt:for-index',
|
||||
key: 'tt:key',
|
||||
type: "tt" /* tt */
|
||||
};
|
||||
exports.Adapter = weixinAdapter;
|
||||
function setAdapter(adapter) {
|
||||
switch (adapter.toLowerCase()) {
|
||||
case "swan" /* swan */:
|
||||
exports.Adapter = swanAdapter;
|
||||
break;
|
||||
case "alipay" /* alipay */:
|
||||
exports.Adapter = alipayAdapter;
|
||||
break;
|
||||
case "tt" /* tt */:
|
||||
exports.Adapter = ttAdapter;
|
||||
break;
|
||||
default:
|
||||
exports.Adapter = weixinAdapter;
|
||||
break;
|
||||
}
|
||||
}
|
||||
exports.setAdapter = setAdapter;
|
||||
//# sourceMappingURL=adapter.js.map
|
|
@ -0,0 +1 @@
|
|||
{"version":3,"file":"adapter.js","sourceRoot":"","sources":["../../src/adapter.ts"],"names":[],"mappings":";;AAAA,IAAkB,QAMjB;AAND,WAAkB,QAAQ;IACxB,2BAAe,CAAA;IACf,yBAAa,CAAA;IACb,6BAAiB,CAAA;IACjB,iCAAqB,CAAA;IACrB,qBAAS,CAAA;AACX,CAAC,EANiB,QAAQ,GAAR,gBAAQ,KAAR,gBAAQ,QAMzB;AAaD,MAAM,aAAa,GAAY;IAC7B,EAAE,EAAE,OAAO;IACX,IAAI,EAAE,SAAS;IACf,MAAM,EAAE,SAAS;IACjB,GAAG,EAAE,QAAQ;IACb,OAAO,EAAE,aAAa;IACtB,QAAQ,EAAE,cAAc;IACxB,GAAG,EAAE,QAAQ;IACb,IAAI,qBAAgB;CACrB,CAAA;AAED,MAAM,WAAW,GAAY;IAC3B,EAAE,EAAE,MAAM;IACV,IAAI,EAAE,QAAQ;IACd,MAAM,EAAE,QAAQ;IAChB,GAAG,EAAE,OAAO;IACZ,OAAO,EAAE,YAAY;IACrB,QAAQ,EAAE,aAAa;IACvB,GAAG,EAAE,OAAO;IACZ,IAAI,mBAAe;CACpB,CAAA;AAED,MAAM,aAAa,GAAY;IAC7B,EAAE,EAAE,MAAM;IACV,IAAI,EAAE,QAAQ;IACd,MAAM,EAAE,QAAQ;IAChB,GAAG,EAAE,OAAO;IACZ,OAAO,EAAE,YAAY;IACrB,QAAQ,EAAE,aAAa;IACvB,GAAG,EAAE,OAAO;IACZ,IAAI,uBAAiB;CACtB,CAAA;AAED,MAAM,SAAS,GAAY;IACzB,EAAE,EAAE,OAAO;IACX,IAAI,EAAE,SAAS;IACf,MAAM,EAAE,SAAS;IACjB,GAAG,EAAE,QAAQ;IACb,OAAO,EAAE,aAAa;IACtB,QAAQ,EAAE,cAAc;IACxB,GAAG,EAAE,QAAQ;IACb,IAAI,eAAa;CAClB,CAAA;AAEU,QAAA,OAAO,GAAY,aAAa,CAAA;AAE3C,SAAgB,UAAU,CAAE,OAAiB;IAC3C,QAAQ,OAAO,CAAC,WAAW,EAAE,EAAE;QAC7B;YACE,eAAO,GAAG,WAAW,CAAA;YACrB,MAAK;QACP;YACE,eAAO,GAAG,aAAa,CAAA;YACvB,MAAK;QACP;YACE,eAAO,GAAG,SAAS,CAAA;YACnB,MAAK;QACP;YACE,eAAO,GAAG,aAAa,CAAA;YACvB,MAAK;KACR;AACH,CAAC;AAfD,gCAeC"}
|
|
@ -0,0 +1,641 @@
|
|||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const t = require("babel-types");
|
||||
const utils_1 = require("./utils");
|
||||
const constant_1 = require("./constant");
|
||||
const lodash_1 = require("lodash");
|
||||
const render_1 = require("./render");
|
||||
const jsx_1 = require("./jsx");
|
||||
const adapter_1 = require("./adapter");
|
||||
const babel_generator_1 = require("babel-generator");
|
||||
function buildConstructor() {
|
||||
const ctor = t.classMethod('constructor', t.identifier('constructor'), [t.identifier('props')], t.blockStatement([
|
||||
t.expressionStatement(t.callExpression(t.identifier('super'), [
|
||||
t.identifier('props')
|
||||
]))
|
||||
]));
|
||||
return ctor;
|
||||
}
|
||||
function processThisPropsFnMemberProperties(member, path, args, binded) {
|
||||
const propertyArray = [];
|
||||
function traverseMember(member) {
|
||||
const object = member.object;
|
||||
const property = member.property;
|
||||
if (t.isIdentifier(property)) {
|
||||
propertyArray.push(property.name);
|
||||
}
|
||||
if (t.isMemberExpression(object)) {
|
||||
if (t.isThisExpression(object.object) &&
|
||||
t.isIdentifier(object.property) &&
|
||||
object.property.name === 'props') {
|
||||
if ("alipay" /* alipay */ === adapter_1.Adapter.type) {
|
||||
if (binded)
|
||||
args.shift();
|
||||
path.replaceWith(t.callExpression(t.memberExpression(t.thisExpression(), t.identifier('__triggerPropsFn')), [
|
||||
t.stringLiteral(propertyArray.reverse().join('.')),
|
||||
t.arrayExpression(args)
|
||||
]));
|
||||
}
|
||||
else {
|
||||
path.replaceWith(t.callExpression(t.memberExpression(t.thisExpression(), t.identifier('__triggerPropsFn')), [t.stringLiteral(propertyArray.reverse().join('.')), t.callExpression(t.memberExpression(t.arrayExpression([t.nullLiteral()]), t.identifier('concat')), [t.arrayExpression(args)])]));
|
||||
}
|
||||
}
|
||||
traverseMember(object);
|
||||
}
|
||||
}
|
||||
traverseMember(member);
|
||||
}
|
||||
class Transformer {
|
||||
constructor(path, sourcePath, componentProperies,
|
||||
//@fix add arg => componentSourceMap
|
||||
componentSourceMap) {
|
||||
this.result = {
|
||||
template: '',
|
||||
components: [],
|
||||
componentProperies: []
|
||||
};
|
||||
this.methods = new Map();
|
||||
this.initState = new Set();
|
||||
this.jsxReferencedIdentifiers = new Set();
|
||||
this.customComponents = new Map();
|
||||
this.anonymousMethod = new Map();
|
||||
this.renderMethod = null;
|
||||
this.customComponentNames = new Set();
|
||||
this.usedState = new Set();
|
||||
this.loopStateName = new Map();
|
||||
this.customComponentData = [];
|
||||
this.refs = [];
|
||||
this.loopRefs = new Map();
|
||||
this.anonymousFuncCounter = utils_1.incrementId();
|
||||
this.buildPropsAnonymousFunc = (attr, expr, isBind = false) => {
|
||||
const { code } = babel_generator_1.default(expr);
|
||||
if (code.startsWith('this.props')) {
|
||||
const methodName = utils_1.findMethodName(expr);
|
||||
const hasMethodName = this.anonymousMethod.has(methodName) || !methodName;
|
||||
const funcName = hasMethodName
|
||||
? this.anonymousMethod.get(methodName)
|
||||
// 测试时使用1个稳定的 uniqueID 便于测试,实际使用5个英文字母,否则小程序不支持
|
||||
: process.env.NODE_ENV === 'test' ? lodash_1.uniqueId('funPrivate') : `funPrivate${utils_1.createRandomLetters(5)}`;
|
||||
this.anonymousMethod.set(methodName, funcName);
|
||||
const newVal = isBind
|
||||
? t.callExpression(t.memberExpression(t.memberExpression(t.thisExpression(), t.identifier(funcName)), t.identifier('bind')), expr.arguments || [])
|
||||
: t.memberExpression(t.thisExpression(), t.identifier(funcName));
|
||||
attr.get('value.expression').replaceWith(newVal);
|
||||
this.methods.set(funcName, null);
|
||||
this.componentProperies.add(methodName);
|
||||
if (hasMethodName) {
|
||||
return;
|
||||
}
|
||||
const attrName = attr.node.name;
|
||||
if (t.isJSXIdentifier(attrName) && attrName.name.startsWith('on')) {
|
||||
this.componentProperies.add(`__fn_${attrName.name}`);
|
||||
}
|
||||
if (methodName.startsWith('on')) {
|
||||
this.componentProperies.add(`__fn_${methodName}`);
|
||||
}
|
||||
const method = t.classMethod('method', t.identifier(funcName), [], t.blockStatement([
|
||||
t.expressionStatement(t.callExpression(t.memberExpression(t.thisExpression(), t.identifier('__triggerPropsFn')), [t.stringLiteral(methodName), t.arrayExpression([t.spreadElement(t.identifier('arguments'))])]))
|
||||
]));
|
||||
this.classPath.node.body.body = this.classPath.node.body.body.concat(method);
|
||||
}
|
||||
};
|
||||
this.classPath = path;
|
||||
this.sourcePath = sourcePath;
|
||||
//@fix add arg => componentSourceMap
|
||||
this.componentSourceMap = componentSourceMap;
|
||||
for (let key of this.componentSourceMap) {
|
||||
const arr = key[0].split('/');
|
||||
if (arr[arr.length - 1].indexOf('-') !== -1 && key[0].indexOf('components/') !== -1 && key[1].length === 0) {
|
||||
this.customComponents.set(arr[arr.length - 1], {
|
||||
sourcePath: key[0],
|
||||
type: 'default'
|
||||
});
|
||||
}
|
||||
}
|
||||
this.moduleNames = Object.keys(path.scope.getAllBindings('module'));
|
||||
this.componentProperies = new Set(componentProperies);
|
||||
this.compile();
|
||||
}
|
||||
setMultipleSlots() {
|
||||
const body = this.classPath.node.body.body;
|
||||
if (body.some(c => t.isClassProperty(c) && c.key.name === 'multipleSlots')) {
|
||||
return;
|
||||
}
|
||||
const multipleSlots = t.classProperty(t.identifier('multipleSlots'), t.booleanLiteral(true));
|
||||
multipleSlots.static = true;
|
||||
body.push(multipleSlots);
|
||||
}
|
||||
createStringRef(componentName, id, refName) {
|
||||
this.refs.push({
|
||||
type: constant_1.DEFAULT_Component_SET.has(componentName) ? 'dom' : 'component',
|
||||
id,
|
||||
refName
|
||||
});
|
||||
}
|
||||
createFunctionRef(componentName, id, fn) {
|
||||
this.refs.push({
|
||||
type: constant_1.DEFAULT_Component_SET.has(componentName) ? 'dom' : 'component',
|
||||
id,
|
||||
fn
|
||||
});
|
||||
}
|
||||
handleRefs() {
|
||||
const objExpr = this.refs.map(ref => {
|
||||
return t.objectExpression([
|
||||
t.objectProperty(t.identifier('type'), t.stringLiteral(ref.type)),
|
||||
t.objectProperty(t.identifier('id'), t.stringLiteral(ref.id)),
|
||||
t.objectProperty(t.identifier('refName'), t.stringLiteral(ref.refName || '')),
|
||||
t.objectProperty(t.identifier('fn'), ref.fn ? ref.fn : t.nullLiteral())
|
||||
]);
|
||||
});
|
||||
this.classPath.node.body.body.push(t.classProperty(t.identifier('$$refs'), t.arrayExpression(objExpr)));
|
||||
}
|
||||
traverse() {
|
||||
const self = this;
|
||||
self.classPath.traverse({
|
||||
JSXOpeningElement: (path) => {
|
||||
const jsx = path.node;
|
||||
const attrs = jsx.attributes;
|
||||
if (!t.isJSXIdentifier(jsx.name)) {
|
||||
return;
|
||||
}
|
||||
const loopCallExpr = path.findParent(p => utils_1.isArrayMapCallExpression(p));
|
||||
const componentName = jsx.name.name;
|
||||
const refAttr = jsx_1.findJSXAttrByName(attrs, 'ref');
|
||||
if (!refAttr) {
|
||||
return;
|
||||
}
|
||||
const idAttr = jsx_1.findJSXAttrByName(attrs, 'id');
|
||||
let id = utils_1.createRandomLetters(5);
|
||||
let idExpr;
|
||||
if (!idAttr) {
|
||||
if (loopCallExpr && loopCallExpr.isCallExpression()) {
|
||||
const [func] = loopCallExpr.node.arguments;
|
||||
let indexId = null;
|
||||
if (t.isFunctionExpression(func) || t.isArrowFunctionExpression(func)) {
|
||||
const params = func.params;
|
||||
indexId = params[1];
|
||||
}
|
||||
if (indexId === null || !t.isIdentifier(indexId)) {
|
||||
throw utils_1.codeFrameError(path.node, '在循环中使用 ref 必须暴露循环的第二个参数 `index`');
|
||||
}
|
||||
attrs.push(t.jSXAttribute(t.jSXIdentifier('id'), t.jSXExpressionContainer(t.binaryExpression('+', t.stringLiteral(id), indexId))));
|
||||
}
|
||||
else {
|
||||
attrs.push(t.jSXAttribute(t.jSXIdentifier('id'), t.stringLiteral(id)));
|
||||
}
|
||||
}
|
||||
else {
|
||||
const idValue = idAttr.value;
|
||||
if (t.isStringLiteral(idValue)) {
|
||||
id = idValue.value;
|
||||
}
|
||||
else if (t.isJSXExpressionContainer(idValue)) {
|
||||
if (t.isStringLiteral(idValue.expression)) {
|
||||
id = idValue.expression.value;
|
||||
}
|
||||
else {
|
||||
idExpr = idValue.expression;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (t.isStringLiteral(refAttr.value)) {
|
||||
if (loopCallExpr) {
|
||||
throw utils_1.codeFrameError(refAttr, '循环中的 ref 只能使用函数。');
|
||||
}
|
||||
this.createStringRef(componentName, id, refAttr.value.value);
|
||||
}
|
||||
if (t.isJSXExpressionContainer(refAttr.value)) {
|
||||
const expr = refAttr.value.expression;
|
||||
if (t.isStringLiteral(expr)) {
|
||||
if (loopCallExpr) {
|
||||
throw utils_1.codeFrameError(refAttr, '循环中的 ref 只能使用函数。');
|
||||
}
|
||||
this.createStringRef(componentName, id, expr.value);
|
||||
}
|
||||
else if (t.isArrowFunctionExpression(expr) || t.isMemberExpression(expr)) {
|
||||
const type = constant_1.DEFAULT_Component_SET.has(componentName) ? 'dom' : 'component';
|
||||
if (loopCallExpr) {
|
||||
this.loopRefs.set(path.parentPath.node, {
|
||||
id: idExpr || id,
|
||||
fn: expr,
|
||||
type,
|
||||
component: path.parentPath
|
||||
});
|
||||
}
|
||||
else {
|
||||
this.refs.push({
|
||||
type,
|
||||
id,
|
||||
fn: expr
|
||||
});
|
||||
}
|
||||
}
|
||||
else {
|
||||
throw utils_1.codeFrameError(refAttr, 'ref 仅支持传入字符串、匿名箭头函数和 class 中已声明的函数');
|
||||
}
|
||||
}
|
||||
for (const [index, attr] of attrs.entries()) {
|
||||
if (attr === refAttr) {
|
||||
attrs.splice(index, 1);
|
||||
}
|
||||
}
|
||||
},
|
||||
ClassMethod(path) {
|
||||
const node = path.node;
|
||||
if (t.isIdentifier(node.key)) {
|
||||
const name = node.key.name;
|
||||
self.methods.set(name, path);
|
||||
if (name === 'render') {
|
||||
self.renderMethod = path;
|
||||
path.traverse({
|
||||
ReturnStatement(returnPath) {
|
||||
const arg = returnPath.node.argument;
|
||||
const ifStem = returnPath.findParent(p => p.isIfStatement());
|
||||
if (ifStem && ifStem.isIfStatement() && arg === null) {
|
||||
const consequent = ifStem.get('consequent');
|
||||
if (consequent.isBlockStatement() && consequent.node.body.includes(returnPath.node)) {
|
||||
returnPath.get('argument').replaceWith(t.nullLiteral());
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
if (name === 'constructor') {
|
||||
path.traverse({
|
||||
AssignmentExpression(p) {
|
||||
if (t.isMemberExpression(p.node.left) &&
|
||||
t.isThisExpression(p.node.left.object) &&
|
||||
t.isIdentifier(p.node.left.property) &&
|
||||
p.node.left.property.name === 'data' &&
|
||||
t.isObjectExpression(p.node.right)) {
|
||||
const properties = p.node.right.properties;
|
||||
properties.forEach(p => {
|
||||
if (t.isObjectProperty(p) && t.isIdentifier(p.key)) {
|
||||
self.initState.add(p.key.name);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
IfStatement(path) {
|
||||
const test = path.get('test');
|
||||
const consequent = path.get('consequent');
|
||||
if (utils_1.isContainJSXElement(consequent) && utils_1.hasComplexExpression(test)) {
|
||||
const scope = self.renderMethod && self.renderMethod.scope || path.scope;
|
||||
utils_1.generateAnonymousState(scope, test, self.jsxReferencedIdentifiers, true);
|
||||
}
|
||||
},
|
||||
ClassProperty(path) {
|
||||
const { key: { name }, value } = path.node;
|
||||
if (t.isArrowFunctionExpression(value) || t.isFunctionExpression(value)) {
|
||||
self.methods.set(name, path);
|
||||
}
|
||||
if (name === 'data' && t.isObjectExpression(value)) {
|
||||
value.properties.forEach(p => {
|
||||
if (t.isObjectProperty(p)) {
|
||||
if (t.isIdentifier(p.key)) {
|
||||
self.initState.add(p.key.name);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
JSXExpressionContainer(path) {
|
||||
const attr = path.findParent(p => p.isJSXAttribute());
|
||||
const isFunctionProp = attr && typeof attr.node.name.name === 'string' && attr.node.name.name.startsWith('on');
|
||||
path.traverse({
|
||||
MemberExpression(path) {
|
||||
const sibling = path.getSibling('property');
|
||||
if (path.get('object').isThisExpression() &&
|
||||
(path.get('property').isIdentifier({ name: 'props' }) || path.get('property').isIdentifier({ name: 'data' })) &&
|
||||
sibling.isIdentifier()) {
|
||||
if (!isFunctionProp) {
|
||||
self.usedState.add(sibling.node.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
const expression = path.get('expression');
|
||||
const scope = self.renderMethod && self.renderMethod.scope || path.scope;
|
||||
const calleeExpr = expression.get('callee');
|
||||
const parentPath = path.parentPath;
|
||||
if (utils_1.hasComplexExpression(expression) &&
|
||||
!isFunctionProp &&
|
||||
!(calleeExpr &&
|
||||
calleeExpr.isMemberExpression() &&
|
||||
calleeExpr.get('object').isMemberExpression() &&
|
||||
calleeExpr.get('property').isIdentifier({ name: 'bind' })) // is not bind
|
||||
) {
|
||||
utils_1.generateAnonymousState(scope, expression, self.jsxReferencedIdentifiers);
|
||||
}
|
||||
else {
|
||||
if (parentPath.isJSXAttribute()) {
|
||||
if (!(expression.isMemberExpression() || expression.isIdentifier()) && parentPath.node.name.name === 'key') {
|
||||
utils_1.generateAnonymousState(scope, expression, self.jsxReferencedIdentifiers);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!attr)
|
||||
return;
|
||||
const key = attr.node.name;
|
||||
const value = attr.node.value;
|
||||
if (!t.isJSXIdentifier(key)) {
|
||||
return;
|
||||
}
|
||||
if (t.isJSXIdentifier(key) && key.name.startsWith('on') && t.isJSXExpressionContainer(value)) {
|
||||
const expr = value.expression;
|
||||
if (t.isCallExpression(expr) && t.isMemberExpression(expr.callee) && t.isIdentifier(expr.callee.property, { name: 'bind' })) {
|
||||
self.buildPropsAnonymousFunc(attr, expr, true);
|
||||
}
|
||||
else if (t.isMemberExpression(expr)) {
|
||||
self.buildPropsAnonymousFunc(attr, expr, false);
|
||||
}
|
||||
else if (t.isArrowFunctionExpression(expr)) {
|
||||
const exprPath = attr.get('value.expression');
|
||||
const stemParent = path.getStatementParent();
|
||||
const counter = self.anonymousFuncCounter();
|
||||
const anonymousFuncName = `anonymousFunc${counter}`;
|
||||
const isCatch = utils_1.isContainStopPropagation(exprPath);
|
||||
const classBody = self.classPath.node.body.body;
|
||||
const loopCallExpr = path.findParent(p => utils_1.isArrayMapCallExpression(p));
|
||||
let index;
|
||||
if (loopCallExpr) {
|
||||
index = lodash_1.get(loopCallExpr, 'node.arguments[0].params[1]');
|
||||
if (!t.isIdentifier(index)) {
|
||||
index = t.identifier('__index' + counter);
|
||||
lodash_1.set(loopCallExpr, 'node.arguments[0].params[1]', index);
|
||||
}
|
||||
classBody.push(t.classProperty(t.identifier(anonymousFuncName + 'Array'), t.arrayExpression([])));
|
||||
const arrayFunc = t.memberExpression(t.memberExpression(t.thisExpression(), t.identifier(anonymousFuncName + 'Array')), t.identifier(index.name), true);
|
||||
classBody.push(t.classMethod('method', t.identifier(anonymousFuncName), [t.identifier(index.name), t.identifier('e')], t.blockStatement([
|
||||
isCatch ? t.expressionStatement(t.callExpression(t.memberExpression(t.identifier('e'), t.identifier('stopPropagation')), [])) : t.emptyStatement(),
|
||||
t.expressionStatement(t.logicalExpression('&&', arrayFunc, t.callExpression(arrayFunc, [t.identifier('e')])))
|
||||
])));
|
||||
exprPath.replaceWith(t.callExpression(t.memberExpression(t.memberExpression(t.thisExpression(), t.identifier(anonymousFuncName)), t.identifier('bind')), [t.thisExpression(), t.identifier(index.name)]));
|
||||
stemParent.insertBefore(t.expressionStatement(t.assignmentExpression('=', arrayFunc, expr)));
|
||||
}
|
||||
else {
|
||||
classBody.push(t.classMethod('method', t.identifier(anonymousFuncName), [t.identifier('e')], t.blockStatement([
|
||||
isCatch ? t.expressionStatement(t.callExpression(t.memberExpression(t.identifier('e'), t.identifier('stopPropagation')), [])) : t.emptyStatement()
|
||||
])));
|
||||
exprPath.replaceWith(t.memberExpression(t.thisExpression(), t.identifier(anonymousFuncName)));
|
||||
stemParent.insertBefore(t.expressionStatement(t.assignmentExpression('=', t.memberExpression(t.thisExpression(), t.identifier(anonymousFuncName)), expr)));
|
||||
}
|
||||
}
|
||||
else {
|
||||
throw utils_1.codeFrameError(path.node, '组件事件传参只能在使用匿名箭头函数,或使用类作用域下的确切引用(this.handleXX || this.props.handleXX),或使用 bind。');
|
||||
}
|
||||
}
|
||||
const jsx = path.findParent(p => p.isJSXOpeningElement());
|
||||
if (!jsx)
|
||||
return;
|
||||
const jsxName = jsx.node.name;
|
||||
if (!t.isJSXIdentifier(jsxName))
|
||||
return;
|
||||
if (expression.isJSXElement())
|
||||
return;
|
||||
if (constant_1.DEFAULT_Component_SET.has(jsxName.name) || expression.isIdentifier() || expression.isMemberExpression() || expression.isLiteral() || expression.isLogicalExpression() || expression.isConditionalExpression() || key.name.startsWith('on') || expression.isCallExpression())
|
||||
return;
|
||||
utils_1.generateAnonymousState(scope, expression, self.jsxReferencedIdentifiers);
|
||||
},
|
||||
JSXElement(path) {
|
||||
const id = path.node.openingElement.name;
|
||||
//@fix extrat xx-xx components to customComponents list
|
||||
if (t.isJSXIdentifier(id) && id.name.indexOf('-') !== -1) {
|
||||
for (let key of self.componentSourceMap) {
|
||||
const arr = key[0].split('/');
|
||||
if (arr[arr.length - 1] === id.name && key[1].length === 0) {
|
||||
self.customComponents.set(id.name, {
|
||||
sourcePath: key[0],
|
||||
type: 'default'
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
if (t.isJSXIdentifier(id) &&
|
||||
!constant_1.DEFAULT_Component_SET.has(id.name) &&
|
||||
self.moduleNames.indexOf(id.name) !== -1) {
|
||||
const name = id.name;
|
||||
const binding = self.classPath.scope.getBinding(name);
|
||||
if (binding && t.isImportDeclaration(binding.path.parent)) {
|
||||
const sourcePath = binding.path.parent.source.value;
|
||||
if (binding.path.isImportDefaultSpecifier()) {
|
||||
self.customComponents.set(name, {
|
||||
sourcePath,
|
||||
type: 'default'
|
||||
});
|
||||
}
|
||||
else {
|
||||
self.customComponents.set(name, {
|
||||
sourcePath,
|
||||
type: 'pattern'
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
MemberExpression: (path) => {
|
||||
const object = path.get('object');
|
||||
const property = path.get('property');
|
||||
if (!(object.isThisExpression() && property.isIdentifier({ name: 'props' }))) {
|
||||
return;
|
||||
}
|
||||
const parentPath = path.parentPath;
|
||||
if (parentPath.isMemberExpression()) {
|
||||
const siblingProp = parentPath.get('property');
|
||||
if (siblingProp.isIdentifier()) {
|
||||
const name = siblingProp.node.name;
|
||||
if (name === 'children') {
|
||||
parentPath.replaceWith(t.jSXElement(t.jSXOpeningElement(t.jSXIdentifier('slot'), [], true), t.jSXClosingElement(t.jSXIdentifier('slot')), [], true));
|
||||
}
|
||||
else if (/^render[A-Z]/.test(name)) {
|
||||
const slotName = utils_1.getSlotName(name);
|
||||
parentPath.replaceWith(t.jSXElement(t.jSXOpeningElement(t.jSXIdentifier('slot'), [
|
||||
t.jSXAttribute(t.jSXIdentifier('name'), t.stringLiteral(slotName))
|
||||
], true), t.jSXClosingElement(t.jSXIdentifier('slot')), []));
|
||||
this.setMultipleSlots();
|
||||
}
|
||||
else {
|
||||
self.componentProperies.add(siblingProp.node.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (parentPath.isVariableDeclarator()) {
|
||||
const siblingId = parentPath.get('id');
|
||||
if (siblingId.isObjectPattern()) {
|
||||
const properties = siblingId.node.properties;
|
||||
for (const prop of properties) {
|
||||
if (t.isRestProperty(prop)) {
|
||||
throw utils_1.codeFrameError(prop.loc, 'this.props 不支持使用 rest property 语法,请把每一个 prop 都单独列出来');
|
||||
}
|
||||
else if (t.isIdentifier(prop.key)) {
|
||||
self.componentProperies.add(prop.key.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
CallExpression(path) {
|
||||
const node = path.node;
|
||||
const callee = node.callee;
|
||||
if (t.isMemberExpression(callee) && t.isMemberExpression(callee.object)) {
|
||||
const property = callee.property;
|
||||
if (t.isIdentifier(property)) {
|
||||
if (property.name.startsWith('on')) {
|
||||
self.componentProperies.add(`__fn_${property.name}`);
|
||||
processThisPropsFnMemberProperties(callee, path, node.arguments, false);
|
||||
}
|
||||
else if (property.name === 'call' || property.name === 'apply') {
|
||||
self.componentProperies.add(`__fn_${property.name}`);
|
||||
processThisPropsFnMemberProperties(callee.object, path, node.arguments, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
setComponents() {
|
||||
this.customComponents.forEach((component, name) => {
|
||||
this.result.components.push({
|
||||
path: utils_1.pathResolver(component.sourcePath, this.sourcePath),
|
||||
name: lodash_1.kebabCase(name),
|
||||
type: component.type
|
||||
});
|
||||
});
|
||||
}
|
||||
setMethods() {
|
||||
const methods = this.classPath.get('body').get('body');
|
||||
for (const method of methods) {
|
||||
if (method.isClassMethod()) {
|
||||
const key = method.get('key');
|
||||
if (key.isIdentifier()) {
|
||||
this.methods.set(key.node.name, method);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
resetConstructor() {
|
||||
const body = this.classPath.node.body.body;
|
||||
if (!this.methods.has('constructor')) {
|
||||
const ctor = buildConstructor();
|
||||
body.unshift(ctor);
|
||||
}
|
||||
if (process.env.NODE_ENV === 'test') {
|
||||
return;
|
||||
}
|
||||
for (const method of body) {
|
||||
if (t.isClassMethod(method) && method.kind === 'constructor') {
|
||||
method.kind = 'method';
|
||||
method.key = t.identifier('_constructor');
|
||||
if (t.isBlockStatement(method.body)) {
|
||||
for (const statement of method.body.body) {
|
||||
if (t.isExpressionStatement(statement)) {
|
||||
const expr = statement.expression;
|
||||
if (t.isCallExpression(expr) && (t.isIdentifier(expr.callee, { name: 'super' }) || t.isSuper(expr.callee))) {
|
||||
expr.callee = t.memberExpression(t.identifier('super'), t.identifier('_constructor'));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
handleLifecyclePropParam(propParam, properties) {
|
||||
let propsName = null;
|
||||
if (!propParam) {
|
||||
return null;
|
||||
}
|
||||
if (t.isIdentifier(propParam)) {
|
||||
propsName = propParam.name;
|
||||
}
|
||||
else if (t.isObjectPattern(propParam)) {
|
||||
for (const prop of propParam.properties) {
|
||||
if (t.isObjectProperty(prop) && t.isIdentifier(prop.key)) {
|
||||
properties.add(prop.key.name);
|
||||
}
|
||||
else if (t.isRestProperty(prop) && t.isIdentifier(prop.argument)) {
|
||||
propsName = prop.argument.name;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
throw utils_1.codeFrameError(propParam.loc, '此生命周期的第一个参数只支持写标识符或对象解构');
|
||||
}
|
||||
return propsName;
|
||||
}
|
||||
findMoreProps() {
|
||||
// 第一个参数是 props 的生命周期
|
||||
const lifeCycles = new Set([
|
||||
// 'constructor',
|
||||
'componentDidUpdate',
|
||||
'shouldComponentUpdate',
|
||||
'getDerivedStateFromProps',
|
||||
'getSnapshotBeforeUpdate',
|
||||
'componentWillReceiveProps',
|
||||
'componentWillUpdate'
|
||||
]);
|
||||
const properties = new Set();
|
||||
this.methods.forEach((method, name) => {
|
||||
if (!lifeCycles.has(name)) {
|
||||
return;
|
||||
}
|
||||
const node = method.node;
|
||||
let propsName = null;
|
||||
if (t.isClassMethod(node)) {
|
||||
propsName = this.handleLifecyclePropParam(node.params[0], properties);
|
||||
}
|
||||
else if (t.isArrowFunctionExpression(node.value) || t.isFunctionExpression(node.value)) {
|
||||
propsName = this.handleLifecyclePropParam(node.value.params[0], properties);
|
||||
}
|
||||
if (propsName === null) {
|
||||
return;
|
||||
}
|
||||
method.traverse({
|
||||
MemberExpression(path) {
|
||||
if (!path.isReferencedMemberExpression()) {
|
||||
return;
|
||||
}
|
||||
const { object, property } = path.node;
|
||||
if (t.isIdentifier(object, { name: propsName }) && t.isIdentifier(property)) {
|
||||
properties.add(property.name);
|
||||
}
|
||||
},
|
||||
VariableDeclarator(path) {
|
||||
const { id, init } = path.node;
|
||||
if (t.isObjectPattern(id) && t.isIdentifier(init, { name: propsName })) {
|
||||
for (const prop of id.properties) {
|
||||
if (t.isObjectProperty(prop) && t.isIdentifier(prop.key)) {
|
||||
properties.add(prop.key.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
properties.forEach((value) => {
|
||||
this.componentProperies.add(value);
|
||||
});
|
||||
});
|
||||
}
|
||||
parseRender() {
|
||||
if (this.renderMethod) {
|
||||
this.result.template = this.result.template
|
||||
+ new render_1.RenderParser(this.renderMethod, this.methods, this.initState, this.jsxReferencedIdentifiers, this.usedState, this.loopStateName, this.customComponentNames, this.customComponentData, this.componentProperies, this.loopRefs).outputTemplate;
|
||||
}
|
||||
}
|
||||
compile() {
|
||||
this.traverse();
|
||||
this.setMethods();
|
||||
this.setComponents();
|
||||
this.resetConstructor();
|
||||
this.findMoreProps();
|
||||
this.handleRefs();
|
||||
this.parseRender();
|
||||
this.result.componentProperies = [...this.componentProperies];
|
||||
}
|
||||
}
|
||||
exports.Transformer = Transformer;
|
||||
//# sourceMappingURL=class.js.map
|
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,94 @@
|
|||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.THIRD_PARTY_COMPONENTS = new Set();
|
||||
// tslint:disable-next-line:variable-name
|
||||
exports.DEFAULT_Component_SET = new Set([
|
||||
'view',
|
||||
'scroll-view',
|
||||
'swiper',
|
||||
'movable-view',
|
||||
'cover-view',
|
||||
'cover-image',
|
||||
'icon',
|
||||
'text',
|
||||
'rich-text',
|
||||
'progress',
|
||||
'button',
|
||||
'checkbox',
|
||||
'form',
|
||||
'input',
|
||||
'label',
|
||||
'picker',
|
||||
'picker-view',
|
||||
'picker-view-column',
|
||||
'radio',
|
||||
'radio-group',
|
||||
'checkbox-group',
|
||||
'slider',
|
||||
'switch',
|
||||
'textarea',
|
||||
'navigator',
|
||||
'audio',
|
||||
'image',
|
||||
'video',
|
||||
'camera',
|
||||
'live-player',
|
||||
'live-pusher',
|
||||
'map',
|
||||
'canvas',
|
||||
'open-data',
|
||||
'web-view',
|
||||
'swiper-item',
|
||||
'movable-area',
|
||||
'movable-view',
|
||||
'functional-page-navigator',
|
||||
'ad',
|
||||
'block',
|
||||
'import',
|
||||
'official-account'
|
||||
]);
|
||||
exports.INTERNAL_SAFE_GET = 'internal_safe_get';
|
||||
exports.TARO_PACKAGE_NAME = '@tarojs/taro';
|
||||
exports.COMPONENTS_PACKAGE_NAME = '@tarojs/components';
|
||||
exports.REDUX_PACKAGE_NAME = '@tarojs/redux';
|
||||
exports.MOBX_PACKAGE_NAME = '@tarojs/mobx';
|
||||
exports.MAP_CALL_ITERATOR = '__item';
|
||||
exports.INTERNAL_INLINE_STYLE = 'internal_inline_style';
|
||||
exports.INTERNAL_GET_ORIGNAL = 'internal_get_original';
|
||||
exports.GEL_ELEMENT_BY_ID = 'getElementById';
|
||||
exports.LOOP_STATE = '$loopState';
|
||||
exports.LOOP_ORIGINAL = '$original';
|
||||
exports.setLoopOriginal = (s) => exports.LOOP_ORIGINAL = s;
|
||||
exports.LOOP_CALLEE = '$anonymousCallee_';
|
||||
exports.SPECIAL_COMPONENT_PROPS = new Map();
|
||||
exports.SPECIAL_COMPONENT_PROPS.set('Progress', new Set([
|
||||
'activeColor',
|
||||
'backgroundColor'
|
||||
]));
|
||||
exports.IMAGE_COMPONENTS = new Set([
|
||||
'Image',
|
||||
'CoverImage'
|
||||
]);
|
||||
exports.swanSpecialAttrs = {
|
||||
'ScrollView': ['scrollTop', 'scrollLeft', 'scrollIntoView'],
|
||||
'Input': ['value'],
|
||||
'Textarea': ['value'],
|
||||
'MovableView': ['x', 'y'],
|
||||
'Slider': ['value']
|
||||
};
|
||||
exports.ALIPAY_BUBBLE_EVENTS = new Set([
|
||||
'onTouchStart',
|
||||
'onTouchMove',
|
||||
'onTouchEnd',
|
||||
'onTouchCancel',
|
||||
'onClick',
|
||||
'onLongTap'
|
||||
]);
|
||||
exports.TRANSFORM_COMPONENT_PROPS = new Map();
|
||||
exports.TRANSFORM_COMPONENT_PROPS.set("alipay" /* alipay */, {
|
||||
'Canvas': {
|
||||
'canvasId': 'id'
|
||||
}
|
||||
});
|
||||
exports.lessThanSignPlacehold = '__LESS_THAN_SIGN_PLACEHOLDER__';
|
||||
//# sourceMappingURL=constant.js.map
|
|
@ -0,0 +1 @@
|
|||
{"version":3,"file":"constant.js","sourceRoot":"","sources":["../../src/constant.ts"],"names":[],"mappings":";;AAEa,QAAA,sBAAsB,GAAG,IAAI,GAAG,EAAU,CAAA;AAEvD,yCAAyC;AAC5B,QAAA,qBAAqB,GAAG,IAAI,GAAG,CAAS;IACnD,MAAM;IACN,YAAY;IACZ,QAAQ;IACR,aAAa;IACb,WAAW;IACX,YAAY;IACZ,MAAM;IACN,MAAM;IACN,UAAU;IACV,UAAU;IACV,QAAQ;IACR,UAAU;IACV,MAAM;IACN,OAAO;IACP,OAAO;IACP,QAAQ;IACR,YAAY;IACZ,kBAAkB;IAClB,OAAO;IACP,YAAY;IACZ,eAAe;IACf,QAAQ;IACR,QAAQ;IACR,UAAU;IACV,WAAW;IACX,OAAO;IACP,OAAO;IACP,OAAO;IACP,QAAQ;IACR,YAAY;IACZ,YAAY;IACZ,KAAK;IACL,QAAQ;IACR,UAAU;IACV,SAAS;IACT,YAAY;IACZ,aAAa;IACb,aAAa;IACb,yBAAyB;IACzB,IAAI;IACJ,OAAO;IACP,QAAQ;IACR,iBAAiB;CAClB,CAAC,CAAA;AAEW,QAAA,iBAAiB,GAAG,mBAAmB,CAAA;AAEvC,QAAA,iBAAiB,GAAG,cAAc,CAAA;AAElC,QAAA,uBAAuB,GAAG,oBAAoB,CAAA;AAE9C,QAAA,kBAAkB,GAAG,eAAe,CAAA;AAEpC,QAAA,iBAAiB,GAAG,cAAc,CAAA;AAElC,QAAA,iBAAiB,GAAG,QAAQ,CAAA;AAE5B,QAAA,qBAAqB,GAAG,uBAAuB,CAAA;AAE/C,QAAA,oBAAoB,GAAG,uBAAuB,CAAA;AAE9C,QAAA,iBAAiB,GAAG,gBAAgB,CAAA;AAEpC,QAAA,UAAU,GAAG,YAAY,CAAA;AAE3B,QAAA,aAAa,GAAG,WAAW,CAAA;AAEzB,QAAA,eAAe,GAAG,CAAC,CAAS,EAAE,EAAE,CAAC,qBAAa,GAAG,CAAC,CAAA;AAElD,QAAA,WAAW,GAAG,mBAAmB,CAAA;AAEjC,QAAA,uBAAuB,GAAG,IAAI,GAAG,EAAuB,CAAA;AAErE,+BAAuB,CAAC,GAAG,CACzB,UAAU,EACV,IAAI,GAAG,CAAC;IACN,aAAa;IACb,iBAAiB;CAClB,CAAC,CACH,CAAA;AAEY,QAAA,gBAAgB,GAAG,IAAI,GAAG,CAAS;IAC9C,OAAO;IACP,YAAY;CACb,CAAC,CAAA;AAEW,QAAA,gBAAgB,GAAG;IAC9B,YAAY,EAAE,CAAC,WAAW,EAAE,YAAY,EAAE,gBAAgB,CAAC;IAC3D,OAAO,EAAE,CAAC,OAAO,CAAC;IAClB,UAAU,EAAE,CAAC,OAAO,CAAC;IACrB,aAAa,EAAE,CAAC,GAAG,EAAE,GAAG,CAAC;IACzB,QAAQ,EAAE,CAAC,OAAO,CAAC;CACpB,CAAA;AAEY,QAAA,oBAAoB,GAAG,IAAI,GAAG,CAAS;IAClD,cAAc;IACd,aAAa;IACb,YAAY;IACZ,eAAe;IACf,SAAS;IACT,WAAW;CACZ,CAAC,CAAA;AAEW,QAAA,yBAAyB,GAAG,IAAI,GAAG,EAA0D,CAAA;AAE1G,iCAAyB,CAAC,GAAG,wBAAkB;IAC7C,QAAQ,EAAE;QACR,UAAU,EAAE,IAAI;KACjB;CACF,CAAC,CAAA;AAEW,QAAA,qBAAqB,GAAG,gCAAgC,CAAA"}
|
|
@ -0,0 +1,43 @@
|
|||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const voidHtmlTags = new Set([
|
||||
// 'image',
|
||||
'img',
|
||||
'input',
|
||||
'import'
|
||||
]);
|
||||
if (process.env.NODE_ENV === 'test') {
|
||||
voidHtmlTags.add('image');
|
||||
}
|
||||
function stringifyAttributes(input) {
|
||||
const attributes = [];
|
||||
for (const key of Object.keys(input)) {
|
||||
let value = input[key];
|
||||
if (value === false) {
|
||||
continue;
|
||||
}
|
||||
if (Array.isArray(value)) {
|
||||
value = value.join(' ');
|
||||
}
|
||||
let attribute = key;
|
||||
if (value !== true) {
|
||||
attribute += `="${String(value)}"`;
|
||||
}
|
||||
attributes.push(attribute);
|
||||
}
|
||||
return attributes.length > 0 ? ' ' + attributes.join(' ') : '';
|
||||
}
|
||||
exports.createHTMLElement = (options) => {
|
||||
options = Object.assign({
|
||||
name: 'div',
|
||||
attributes: {},
|
||||
value: ''
|
||||
}, options);
|
||||
const isVoidTag = voidHtmlTags.has(options.name);
|
||||
let ret = `<${options.name}${stringifyAttributes(options.attributes)}${isVoidTag ? `/` : ''}>`;
|
||||
if (!isVoidTag) {
|
||||
ret += `${options.value}</${options.name}>`;
|
||||
}
|
||||
return ret;
|
||||
};
|
||||
//# sourceMappingURL=create-html-element.js.map
|
|
@ -0,0 +1 @@
|
|||
{"version":3,"file":"create-html-element.js","sourceRoot":"","sources":["../../src/create-html-element.ts"],"names":[],"mappings":";;AAAA,MAAM,YAAY,GAAG,IAAI,GAAG,CAAS;IACnC,WAAW;IACX,KAAK;IACL,OAAO;IACP,QAAQ;CACT,CAAC,CAAA;AAEF,IAAI,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,MAAM,EAAE;IACnC,YAAY,CAAC,GAAG,CAAC,OAAO,CAAC,CAAA;CAC1B;AAQD,SAAS,mBAAmB,CAAE,KAAa;IACzC,MAAM,UAAU,GAAa,EAAE,CAAA;IAE/B,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE;QACpC,IAAI,KAAK,GAAG,KAAK,CAAC,GAAG,CAAC,CAAA;QAEtB,IAAI,KAAK,KAAK,KAAK,EAAE;YACnB,SAAQ;SACT;QAED,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE;YACxB,KAAK,GAAG,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;SACxB;QAED,IAAI,SAAS,GAAG,GAAG,CAAA;QAEnB,IAAI,KAAK,KAAK,IAAI,EAAE;YAClB,SAAS,IAAI,KAAK,MAAM,CAAC,KAAK,CAAC,GAAG,CAAA;SACnC;QAED,UAAU,CAAC,IAAI,CAAC,SAAS,CAAC,CAAA;KAC3B;IAED,OAAO,UAAU,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,GAAG,UAAU,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAA;AAEhE,CAAC;AAEY,QAAA,iBAAiB,GAAG,CAAC,OAAgB,EAAE,EAAE;IACpD,OAAO,GAAG,MAAM,CAAC,MAAM,CACrB;QACE,IAAI,EAAE,KAAK;QACX,UAAU,EAAE,EAAE;QACd,KAAK,EAAE,EAAE;KACV,EACD,OAAO,CACR,CAAA;IAED,MAAM,SAAS,GAAG,YAAY,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,CAAA;IAEhD,IAAI,GAAG,GAAG,IAAI,OAAO,CAAC,IAAI,GAAG,mBAAmB,CAAC,OAAO,CAAC,UAAU,CAAC,GAAG,SAAS,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAG,GAAG,CAAA;IAE/F,IAAI,CAAC,SAAS,EAAE;QACd,GAAG,IAAI,GAAG,OAAO,CAAC,KAAK,KAAK,OAAO,CAAC,IAAI,GAAG,CAAA;KAC5C;IAED,OAAO,GAAG,CAAA;AACZ,CAAC,CAAA"}
|
|
@ -0,0 +1,47 @@
|
|||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const eslint_1 = require("eslint");
|
||||
const utils_1 = require("./utils");
|
||||
const cli = new eslint_1.CLIEngine({
|
||||
baseConfig: {
|
||||
extends: ['plugin:taro/transformer']
|
||||
},
|
||||
useEslintrc: false,
|
||||
parser: 'babel-eslint',
|
||||
parserOptions: {
|
||||
ecmaVersion: 2018,
|
||||
ecmaFeatures: {
|
||||
jsx: true,
|
||||
legacyDecorators: true
|
||||
}
|
||||
}
|
||||
});
|
||||
exports.eslintValidation = () => {
|
||||
return {
|
||||
visitor: {
|
||||
Program(_, state) {
|
||||
const { file: { code } } = state;
|
||||
const report = cli.executeOnText(code);
|
||||
if (report.errorCount > 0) {
|
||||
for (const result of report.results) {
|
||||
for (const msg of result.messages) {
|
||||
const err = utils_1.codeFrameError({
|
||||
start: {
|
||||
line: msg.line,
|
||||
column: msg.column
|
||||
},
|
||||
end: {
|
||||
line: msg.endLine,
|
||||
column: msg.endColumn
|
||||
}
|
||||
}, msg.message);
|
||||
// tslint:disable-next-line
|
||||
console.warn('\n' + `ESLint(${msg.ruleId}) 错误:` + err.message + '\n');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
//# sourceMappingURL=eslint.js.map
|
|
@ -0,0 +1 @@
|
|||
{"version":3,"file":"eslint.js","sourceRoot":"","sources":["../../src/eslint.ts"],"names":[],"mappings":";;AAAA,mCAAkC;AAElC,mCAAwC;AAExC,MAAM,GAAG,GAAG,IAAI,kBAAS,CAAC;IACxB,UAAU,EAAE;QACV,OAAO,EAAE,CAAC,yBAAyB,CAAC;KACrC;IACD,WAAW,EAAE,KAAK;IAClB,MAAM,EAAE,cAAc;IACtB,aAAa,EAAE;QACb,WAAW,EAAE,IAAI;QACjB,YAAY,EAAE;YACZ,GAAG,EAAE,IAAI;YACT,gBAAgB,EAAE,IAAI;SACvB;KACF;CACF,CAAC,CAAA;AAEW,QAAA,gBAAgB,GAEzB,GAAG,EAAE;IACP,OAAO;QACL,OAAO,EAAE;YACP,OAAO,CAAE,CAAC,EAAE,KAAK;gBACf,MAAM,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,EAAE,GAAG,KAAK,CAAA;gBAChC,MAAM,MAAM,GAAG,GAAG,CAAC,aAAa,CAAC,IAAI,CAAC,CAAA;gBACtC,IAAI,MAAM,CAAC,UAAU,GAAG,CAAC,EAAE;oBACzB,KAAK,MAAM,MAAM,IAAI,MAAM,CAAC,OAAO,EAAE;wBACnC,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,QAAQ,EAAE;4BACjC,MAAM,GAAG,GAAG,sBAAc,CAAC;gCACzB,KAAK,EAAE;oCACL,IAAI,EAAE,GAAG,CAAC,IAAI;oCACd,MAAM,EAAE,GAAG,CAAC,MAAM;iCACnB;gCACD,GAAG,EAAE;oCACH,IAAI,EAAE,GAAG,CAAC,OAAO;oCACjB,MAAM,EAAE,GAAG,CAAC,SAAS;iCACtB;6BACF,EAAE,GAAG,CAAC,OAAO,CAAC,CAAA;4BACf,2BAA2B;4BAC3B,OAAO,CAAC,IAAI,CAAC,IAAI,GAAG,UAAU,GAAG,CAAC,MAAM,OAAO,GAAG,GAAG,CAAC,OAAO,GAAG,IAAI,CAAC,CAAA;yBACtE;qBACF;iBACF;YACH,CAAC;SACF;KACF,CAAA;AACH,CAAC,CAAA"}
|
|
@ -0,0 +1,488 @@
|
|||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const babel_traverse_1 = require("babel-traverse");
|
||||
const babel_generator_1 = require("babel-generator");
|
||||
const html_1 = require("html");
|
||||
const babel_core_1 = require("babel-core");
|
||||
const ts = require("typescript");
|
||||
const class_1 = require("./class");
|
||||
const utils_1 = require("./utils");
|
||||
const t = require("babel-types");
|
||||
const constant_1 = require("./constant");
|
||||
const adapter_1 = require("./adapter");
|
||||
const options_1 = require("./options");
|
||||
const lodash_1 = require("lodash");
|
||||
const template = require('babel-template');
|
||||
function getIdsFromMemberProps(member) {
|
||||
let ids = [];
|
||||
const { object, property } = member;
|
||||
if (t.isMemberExpression(object)) {
|
||||
ids = ids.concat(getIdsFromMemberProps(object));
|
||||
}
|
||||
if (t.isThisExpression(object)) {
|
||||
ids.push('this');
|
||||
}
|
||||
if (t.isIdentifier(object)) {
|
||||
ids.push(object.name);
|
||||
}
|
||||
if (t.isIdentifier(property)) {
|
||||
ids.push(property.name);
|
||||
}
|
||||
return ids;
|
||||
}
|
||||
/**
|
||||
* TS 编译器会把 class property 移到构造器,
|
||||
* 而小程序要求 `config` 和所有函数在初始化(after new Class)之后就收集到所有的函数和 config 信息,
|
||||
* 所以当如构造器里有 this.func = () => {...} 的形式,就给他转换成普通的 classProperty function
|
||||
* 如果有 config 就给他还原
|
||||
*/
|
||||
function resetTSClassProperty(body) {
|
||||
for (const method of body) {
|
||||
if (t.isClassMethod(method) && method.kind === 'constructor') {
|
||||
if (t.isBlockStatement(method.body)) {
|
||||
method.body.body = method.body.body.filter(statement => {
|
||||
if (t.isExpressionStatement(statement) && t.isAssignmentExpression(statement.expression)) {
|
||||
const expr = statement.expression;
|
||||
const { left, right } = expr;
|
||||
if (t.isMemberExpression(left) &&
|
||||
t.isThisExpression(left.object) &&
|
||||
t.isIdentifier(left.property)) {
|
||||
if ((t.isArrowFunctionExpression(right) || t.isFunctionExpression(right))
|
||||
||
|
||||
(left.property.name === 'config' && t.isObjectExpression(right))) {
|
||||
const classProp = t.classProperty(left.property, right);
|
||||
body.push(classProp);
|
||||
handleThirdPartyComponent(classProp);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
function findDeclarationScope(path, id) {
|
||||
const scopePath = path.findParent(p => !!p.scope.getOwnBindingIdentifier(id.name));
|
||||
if (scopePath) {
|
||||
return scopePath;
|
||||
}
|
||||
throw utils_1.codeFrameError(path.node, '该引用从未被定义');
|
||||
}
|
||||
function buildFullPathThisPropsRef(id, memberIds, path) {
|
||||
const scopePath = findDeclarationScope(path, id);
|
||||
const binding = scopePath.scope.getOwnBinding(id.name);
|
||||
if (binding) {
|
||||
const bindingPath = binding.path;
|
||||
if (bindingPath.isVariableDeclarator()) {
|
||||
const dclId = bindingPath.get('id');
|
||||
const dclInit = bindingPath.get('init');
|
||||
let dclInitIds = [];
|
||||
if (dclInit.isMemberExpression()) {
|
||||
dclInitIds = getIdsFromMemberProps(dclInit.node);
|
||||
if (dclId.isIdentifier()) {
|
||||
memberIds.shift();
|
||||
}
|
||||
if (dclInitIds[0] === 'this' && dclInitIds[1] === 'props') {
|
||||
return template(dclInitIds.concat(memberIds).join('.'))().expression;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
function handleThirdPartyComponent(expr) {
|
||||
if (t.isClassProperty(expr) && expr.key.name === 'config' && t.isObjectExpression(expr.value)) {
|
||||
const properties = expr.value.properties;
|
||||
for (const prop of properties) {
|
||||
if (t.isObjectProperty(prop) &&
|
||||
(t.isIdentifier(prop.key, { name: 'usingComponents' }) || t.isStringLiteral(prop.key, { value: 'usingComponents' })) &&
|
||||
t.isObjectExpression(prop.value)) {
|
||||
for (const value of prop.value.properties) {
|
||||
if (t.isObjectProperty(value)) {
|
||||
if (t.isStringLiteral(value.key)) {
|
||||
constant_1.THIRD_PARTY_COMPONENTS.add(value.key.value);
|
||||
}
|
||||
if (t.isIdentifier(value.key)) {
|
||||
constant_1.THIRD_PARTY_COMPONENTS.add(value.key.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
function transform(options) {
|
||||
if (options.adapter) {
|
||||
adapter_1.setAdapter(options.adapter);
|
||||
}
|
||||
if (adapter_1.Adapter.type === "swan" /* swan */) {
|
||||
constant_1.setLoopOriginal('privateOriginal');
|
||||
}
|
||||
constant_1.THIRD_PARTY_COMPONENTS.clear();
|
||||
const code = options.isTyped
|
||||
? ts.transpile(options.code, {
|
||||
jsx: ts.JsxEmit.Preserve,
|
||||
target: ts.ScriptTarget.ESNext,
|
||||
importHelpers: true,
|
||||
noEmitHelpers: true
|
||||
})
|
||||
: options.code;
|
||||
options.env = Object.assign({ 'process.env.TARO_ENV': options.adapter || 'weapp' }, options.env || {});
|
||||
options_1.setTransformOptions(options);
|
||||
utils_1.setting.sourceCode = code;
|
||||
// babel-traverse 无法生成 Hub
|
||||
// 导致 Path#getSource|buildCodeFrameError 都无法直接使用
|
||||
// 原因大概是 babylon.parse 没有生成 File 实例导致 scope 和 path 原型上都没有 `file`
|
||||
// 将来升级到 babel@7 可以直接用 parse 而不是 transform
|
||||
const ast = babel_core_1.transform(code, options_1.buildBabelTransformOptions()).ast;
|
||||
if (options.isNormal) {
|
||||
return { ast };
|
||||
}
|
||||
// transformFromAst(ast, code)
|
||||
let result;
|
||||
const componentSourceMap = new Map();
|
||||
const imageSource = new Set();
|
||||
const importSources = new Set();
|
||||
let componentProperies = [];
|
||||
let mainClass;
|
||||
let storeName;
|
||||
let renderMethod;
|
||||
let isImportTaro = false;
|
||||
babel_traverse_1.default(ast, {
|
||||
TemplateLiteral(path) {
|
||||
const nodes = [];
|
||||
const { quasis, expressions } = path.node;
|
||||
let index = 0;
|
||||
if (path.parentPath.isTaggedTemplateExpression()) {
|
||||
return;
|
||||
}
|
||||
for (const elem of quasis) {
|
||||
if (elem.value.cooked) {
|
||||
nodes.push(t.stringLiteral(elem.value.cooked));
|
||||
}
|
||||
if (index < expressions.length) {
|
||||
const expr = expressions[index++];
|
||||
if (!t.isStringLiteral(expr, { value: '' })) {
|
||||
nodes.push(expr);
|
||||
}
|
||||
}
|
||||
}
|
||||
// + 号连接符必须保证第一和第二个 node 都是字符串
|
||||
if (!t.isStringLiteral(nodes[0]) && !t.isStringLiteral(nodes[1])) {
|
||||
nodes.unshift(t.stringLiteral(''));
|
||||
}
|
||||
let root = nodes[0];
|
||||
for (let i = 1; i < nodes.length; i++) {
|
||||
root = t.binaryExpression('+', root, nodes[i]);
|
||||
}
|
||||
path.replaceWith(root);
|
||||
},
|
||||
ClassDeclaration(path) {
|
||||
mainClass = path;
|
||||
const superClass = utils_1.getSuperClassCode(path);
|
||||
if (superClass) {
|
||||
try {
|
||||
componentProperies = transform({
|
||||
isRoot: false,
|
||||
isApp: false,
|
||||
code: superClass.code,
|
||||
isTyped: true,
|
||||
sourcePath: superClass.sourcePath,
|
||||
outputPath: superClass.sourcePath
|
||||
}).componentProperies;
|
||||
}
|
||||
catch (error) {
|
||||
//
|
||||
}
|
||||
}
|
||||
},
|
||||
ClassExpression(path) {
|
||||
mainClass = path;
|
||||
},
|
||||
ClassMethod(path) {
|
||||
if (t.isIdentifier(path.node.key) && path.node.key.name === 'render') {
|
||||
renderMethod = path;
|
||||
}
|
||||
},
|
||||
IfStatement(path) {
|
||||
const consequent = path.get('consequent');
|
||||
if (!consequent.isBlockStatement()) {
|
||||
consequent.replaceWith(t.blockStatement([
|
||||
consequent.node
|
||||
]));
|
||||
}
|
||||
},
|
||||
CallExpression(path) {
|
||||
const callee = path.get('callee');
|
||||
if (utils_1.isContainJSXElement(path)) {
|
||||
return;
|
||||
}
|
||||
if (callee.isReferencedMemberExpression()) {
|
||||
const id = utils_1.findFirstIdentifierFromMemberExpression(callee.node);
|
||||
const property = callee.node.property;
|
||||
if (t.isIdentifier(property) && property.name.startsWith('on')) {
|
||||
const funcExpr = path.findParent(p => p.isFunctionExpression());
|
||||
if (funcExpr && funcExpr.isFunctionExpression()) {
|
||||
const taroAPI = funcExpr.findParent(p => p.isCallExpression() && t.isMemberExpression(p.node.callee) && t.isIdentifier(p.node.callee.object, { name: 'Taro' }));
|
||||
if (taroAPI && taroAPI.isCallExpression()) {
|
||||
throw utils_1.codeFrameError(funcExpr.node, '在回调函数使用从 props 传递的函数时,请把回调函数改造为箭头函数并一直使用 `this` 取值');
|
||||
}
|
||||
}
|
||||
}
|
||||
const calleeIds = getIdsFromMemberProps(callee.node);
|
||||
if (t.isIdentifier(id) && id.name.startsWith('on') && "alipay" /* alipay */ !== adapter_1.Adapter.type) {
|
||||
const fullPath = buildFullPathThisPropsRef(id, calleeIds, path);
|
||||
if (fullPath) {
|
||||
path.replaceWith(t.callExpression(fullPath, path.node.arguments));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (callee.isReferencedIdentifier()) {
|
||||
const id = callee.node;
|
||||
const ids = [id.name];
|
||||
if (t.isIdentifier(id) && id.name.startsWith('on')) {
|
||||
const funcExpr = path.findParent(p => p.isFunctionExpression());
|
||||
if (funcExpr && funcExpr.isFunctionExpression()) {
|
||||
const taroAPI = funcExpr.findParent(p => p.isCallExpression() && t.isMemberExpression(p.node.callee) && t.isIdentifier(p.node.callee.object, { name: 'Taro' }));
|
||||
if (taroAPI && taroAPI.isCallExpression()) {
|
||||
throw utils_1.codeFrameError(funcExpr.node, '在回调函数使用从 props 传递的函数时,请把回调函数改造为箭头函数并一直使用 `this` 取值');
|
||||
}
|
||||
}
|
||||
const fullPath = buildFullPathThisPropsRef(id, ids, path);
|
||||
if (fullPath) {
|
||||
path.replaceWith(t.callExpression(fullPath, path.node.arguments));
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
// JSXIdentifier (path) {
|
||||
// const parentPath = path.parentPath
|
||||
// if (!parentPath.isJSXAttribute()) {
|
||||
// return
|
||||
// }
|
||||
// const element = parentPath.parentPath
|
||||
// if (!element.isJSXOpeningElement()) {
|
||||
// return
|
||||
// }
|
||||
// const elementName = element.get('name')
|
||||
// if (!elementName.isJSXIdentifier()) {
|
||||
// return
|
||||
// }
|
||||
// if (DEFAULT_Component_SET.has(elementName.node.name)) {
|
||||
// return
|
||||
// }
|
||||
// const expr = parentPath.get('value.expression')
|
||||
// },
|
||||
JSXElement(path) {
|
||||
const assignment = path.findParent(p => p.isAssignmentExpression());
|
||||
if (assignment && assignment.isAssignmentExpression() && !options.isTyped) {
|
||||
const left = assignment.node.left;
|
||||
if (t.isIdentifier(left)) {
|
||||
const binding = assignment.scope.getBinding(left.name);
|
||||
if (binding && binding.scope === assignment.scope) {
|
||||
if (binding.path.isVariableDeclarator()) {
|
||||
binding.path.node.init = path.node;
|
||||
assignment.remove();
|
||||
}
|
||||
else {
|
||||
throw utils_1.codeFrameError(path.node, '同一个作用域的JSX 变量延时赋值没有意义。详见:https://github.com/NervJS/taro/issues/550');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
const switchStatement = path.findParent(p => p.isSwitchStatement());
|
||||
if (switchStatement && switchStatement.isSwitchStatement()) {
|
||||
const { discriminant, cases } = switchStatement.node;
|
||||
const ifStatement = cases.map((Case, index) => {
|
||||
const [consequent] = Case.consequent;
|
||||
if (!t.isBlockStatement(consequent)) {
|
||||
throw utils_1.codeFrameError(switchStatement.node, '含有 JSX 的 switch case 语句必须每种情况都用花括号 `{}` 包裹结果');
|
||||
}
|
||||
const block = t.blockStatement(consequent.body.filter(b => !t.isBreakStatement(b)));
|
||||
if (index !== cases.length - 1 && t.isNullLiteral(Case.test)) {
|
||||
throw utils_1.codeFrameError(Case, '含有 JSX 的 switch case 语句只有最后一个 case 才能是 default');
|
||||
}
|
||||
const test = Case.test === null ? t.nullLiteral() : t.binaryExpression('===', discriminant, Case.test);
|
||||
return { block, test };
|
||||
}).reduceRight((ifStatement, item) => {
|
||||
if (t.isNullLiteral(item.test)) {
|
||||
ifStatement.alternate = item.block;
|
||||
return ifStatement;
|
||||
}
|
||||
const newStatement = t.ifStatement(item.test, item.block, t.isBooleanLiteral(ifStatement.test, { value: false })
|
||||
? ifStatement.alternate
|
||||
: ifStatement);
|
||||
return newStatement;
|
||||
}, t.ifStatement(t.booleanLiteral(false), t.blockStatement([])));
|
||||
switchStatement.insertAfter(ifStatement);
|
||||
switchStatement.remove();
|
||||
}
|
||||
const isForStatement = (p) => p && (p.isForStatement() || p.isForInStatement() || p.isForOfStatement());
|
||||
const forStatement = path.findParent(isForStatement);
|
||||
if (isForStatement(forStatement)) {
|
||||
throw utils_1.codeFrameError(forStatement.node, '不行使用 for 循环操作 JSX 元素,详情:https://github.com/NervJS/taro/blob/master/packages/eslint-plugin-taro/docs/manipulate-jsx-as-array.md');
|
||||
}
|
||||
const loopCallExpr = path.findParent(p => utils_1.isArrayMapCallExpression(p));
|
||||
if (loopCallExpr && loopCallExpr.isCallExpression()) {
|
||||
const [func] = loopCallExpr.node.arguments;
|
||||
if (t.isArrowFunctionExpression(func) && !t.isBlockStatement(func.body)) {
|
||||
func.body = t.blockStatement([
|
||||
t.returnStatement(func.body)
|
||||
]);
|
||||
}
|
||||
}
|
||||
},
|
||||
JSXOpeningElement(path) {
|
||||
const { name } = path.node.name;
|
||||
if (name === 'Provider') {
|
||||
const modules = path.scope.getAllBindings('module');
|
||||
const providerBinding = Object.values(modules).some((m) => m.identifier.name === 'Provider');
|
||||
if (providerBinding) {
|
||||
path.node.name = t.jSXIdentifier('view');
|
||||
const store = path.node.attributes.find(attr => attr.name.name === 'store');
|
||||
if (store && t.isJSXExpressionContainer(store.value) && t.isIdentifier(store.value.expression)) {
|
||||
storeName = store.value.expression.name;
|
||||
}
|
||||
path.node.attributes = [];
|
||||
}
|
||||
}
|
||||
if (constant_1.IMAGE_COMPONENTS.has(name)) {
|
||||
for (const attr of path.node.attributes) {
|
||||
if (attr.name.name === 'src') {
|
||||
if (t.isStringLiteral(attr.value)) {
|
||||
imageSource.add(attr.value.value);
|
||||
}
|
||||
else if (t.isJSXExpressionContainer(attr.value)) {
|
||||
if (t.isStringLiteral(attr.value.expression)) {
|
||||
imageSource.add(attr.value.expression.value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
JSXAttribute(path) {
|
||||
const { name, value } = path.node;
|
||||
if (!t.isJSXIdentifier(name) || value === null || t.isStringLiteral(value) || t.isJSXElement(value)) {
|
||||
return;
|
||||
}
|
||||
const expr = value.expression;
|
||||
const exprPath = path.get('value.expression');
|
||||
const classDecl = path.findParent(p => p.isClassDeclaration());
|
||||
const classDeclName = classDecl && classDecl.isClassDeclaration() && lodash_1.get(classDecl, 'node.id.name', '');
|
||||
let isConverted = false;
|
||||
if (classDeclName) {
|
||||
isConverted = classDeclName === '_C' || classDeclName.endsWith('Tmpl');
|
||||
}
|
||||
if (!t.isBinaryExpression(expr, { operator: '+' }) && !t.isLiteral(expr) && name.name === 'style' && !isConverted) {
|
||||
const jsxID = path.findParent(p => p.isJSXOpeningElement()).get('name');
|
||||
if (jsxID && jsxID.isJSXIdentifier() && constant_1.DEFAULT_Component_SET.has(jsxID.node.name)) {
|
||||
exprPath.replaceWith(t.callExpression(t.identifier(constant_1.INTERNAL_INLINE_STYLE), [expr]));
|
||||
}
|
||||
}
|
||||
if (name.name.startsWith('on')) {
|
||||
if (exprPath.isReferencedIdentifier()) {
|
||||
const ids = [expr.name];
|
||||
const fullPath = buildFullPathThisPropsRef(expr, ids, path);
|
||||
if (fullPath) {
|
||||
exprPath.replaceWith(fullPath);
|
||||
}
|
||||
}
|
||||
if (exprPath.isReferencedMemberExpression()) {
|
||||
const id = utils_1.findFirstIdentifierFromMemberExpression(expr);
|
||||
const ids = getIdsFromMemberProps(expr);
|
||||
if (t.isIdentifier(id)) {
|
||||
const fullPath = buildFullPathThisPropsRef(id, ids, path);
|
||||
if (fullPath) {
|
||||
exprPath.replaceWith(fullPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
// @TODO: bind 的处理待定
|
||||
}
|
||||
},
|
||||
ImportDeclaration(path) {
|
||||
const source = path.node.source.value;
|
||||
if (importSources.has(source)) {
|
||||
throw utils_1.codeFrameError(path.node, '无法在同一文件重复 import 相同的包。');
|
||||
}
|
||||
else {
|
||||
importSources.add(source);
|
||||
}
|
||||
const names = [];
|
||||
if (source === constant_1.TARO_PACKAGE_NAME) {
|
||||
isImportTaro = true;
|
||||
path.node.specifiers.push(t.importSpecifier(t.identifier(constant_1.INTERNAL_SAFE_GET), t.identifier(constant_1.INTERNAL_SAFE_GET)), t.importSpecifier(t.identifier(constant_1.INTERNAL_GET_ORIGNAL), t.identifier(constant_1.INTERNAL_GET_ORIGNAL)), t.importSpecifier(t.identifier(constant_1.INTERNAL_INLINE_STYLE), t.identifier(constant_1.INTERNAL_INLINE_STYLE)), t.importSpecifier(t.identifier(constant_1.GEL_ELEMENT_BY_ID), t.identifier(constant_1.GEL_ELEMENT_BY_ID)));
|
||||
}
|
||||
if (source === constant_1.REDUX_PACKAGE_NAME || source === constant_1.MOBX_PACKAGE_NAME) {
|
||||
path.node.specifiers.forEach((s, index, specs) => {
|
||||
if (s.local.name === 'Provider') {
|
||||
specs.splice(index, 1);
|
||||
specs.push(t.importSpecifier(t.identifier('setStore'), t.identifier('setStore')));
|
||||
}
|
||||
});
|
||||
}
|
||||
path.traverse({
|
||||
ImportDefaultSpecifier(path) {
|
||||
const name = path.node.local.name;
|
||||
constant_1.DEFAULT_Component_SET.has(name) || names.push(name);
|
||||
},
|
||||
ImportSpecifier(path) {
|
||||
const name = path.node.imported.name;
|
||||
constant_1.DEFAULT_Component_SET.has(name) || names.push(name);
|
||||
if (source === constant_1.TARO_PACKAGE_NAME && name === 'Component') {
|
||||
path.node.local = t.identifier('__BaseComponent');
|
||||
}
|
||||
}
|
||||
});
|
||||
componentSourceMap.set(source, names);
|
||||
}
|
||||
});
|
||||
if (!isImportTaro) {
|
||||
ast.program.body.unshift(t.importDeclaration([
|
||||
t.importDefaultSpecifier(t.identifier('Taro')),
|
||||
t.importSpecifier(t.identifier(constant_1.INTERNAL_SAFE_GET), t.identifier(constant_1.INTERNAL_SAFE_GET)),
|
||||
t.importSpecifier(t.identifier(constant_1.INTERNAL_GET_ORIGNAL), t.identifier(constant_1.INTERNAL_GET_ORIGNAL)),
|
||||
t.importSpecifier(t.identifier(constant_1.INTERNAL_INLINE_STYLE), t.identifier(constant_1.INTERNAL_INLINE_STYLE))
|
||||
], t.stringLiteral('@tarojs/taro')));
|
||||
}
|
||||
if (!mainClass) {
|
||||
throw new Error('未找到 Taro.Component 的类定义');
|
||||
}
|
||||
mainClass.node.body.body.forEach(handleThirdPartyComponent);
|
||||
const storeBinding = mainClass.scope.getBinding(storeName);
|
||||
mainClass.scope.rename('Component', '__BaseComponent');
|
||||
if (storeBinding) {
|
||||
const statementPath = storeBinding.path.getStatementParent();
|
||||
if (statementPath) {
|
||||
ast.program.body.forEach((node, index, body) => {
|
||||
if (node === statementPath.node) {
|
||||
body.splice(index + 1, 0, t.expressionStatement(t.callExpression(t.identifier('setStore'), [
|
||||
t.identifier(storeName)
|
||||
])));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
resetTSClassProperty(mainClass.node.body.body);
|
||||
if (options.isApp) {
|
||||
renderMethod.replaceWith(t.classMethod('method', t.identifier('_createData'), [], t.blockStatement([])));
|
||||
return { ast };
|
||||
}
|
||||
//@fix add arg => componentSourceMap
|
||||
result = new class_1.Transformer(mainClass, options.sourcePath, componentProperies, componentSourceMap).result;
|
||||
result.code = babel_generator_1.default(ast).code;
|
||||
result.ast = ast;
|
||||
const lessThanSignReg = new RegExp(constant_1.lessThanSignPlacehold, 'g');
|
||||
result.compressedTemplate = result.template;
|
||||
result.template = html_1.prettyPrint(result.template, {
|
||||
max_char: 0,
|
||||
unformatted: process.env.NODE_ENV === 'test' ? [] : ['text']
|
||||
});
|
||||
result.template = result.template.replace(lessThanSignReg, '<');
|
||||
result.imageSrcs = Array.from(imageSource);
|
||||
return result;
|
||||
}
|
||||
exports.default = transform;
|
||||
//# sourceMappingURL=index.js.map
|
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,258 @@
|
|||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const babel_generator_1 = require("babel-generator");
|
||||
const t = require("babel-types");
|
||||
const lodash_1 = require("lodash");
|
||||
const constant_1 = require("./constant");
|
||||
const create_html_element_1 = require("./create-html-element");
|
||||
const utils_1 = require("./utils");
|
||||
const adapter_1 = require("./adapter");
|
||||
function isStartWithWX(str) {
|
||||
return str[0] === 'w' && str[1] === 'x';
|
||||
}
|
||||
exports.isStartWithWX = isStartWithWX;
|
||||
const specialComponentName = ['block', 'Block', 'slot', 'Slot'];
|
||||
function removeJSXThisProperty(path) {
|
||||
if (!path.parentPath.isCallExpression()) {
|
||||
const p = path.getSibling('property');
|
||||
if (p.isIdentifier({ name: 'props' }) ||
|
||||
p.isIdentifier({ name: 'data' })) {
|
||||
path.parentPath.replaceWithSourceString('this');
|
||||
}
|
||||
else {
|
||||
path.parentPath.replaceWith(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
exports.removeJSXThisProperty = removeJSXThisProperty;
|
||||
function findJSXAttrByName(attrs, name) {
|
||||
for (const attr of attrs) {
|
||||
if (!t.isJSXIdentifier(attr.name)) {
|
||||
break;
|
||||
}
|
||||
if (attr.name.name === name) {
|
||||
return attr;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
exports.findJSXAttrByName = findJSXAttrByName;
|
||||
function buildRefTemplate(name, refName, loop, key) {
|
||||
const attrs = [
|
||||
t.jSXAttribute(t.jSXIdentifier('is'), t.stringLiteral(name)),
|
||||
t.jSXAttribute(t.jSXIdentifier('data'), t.stringLiteral(`{{...${refName ? `${loop ? '' : '$$'}${refName}` : '__data'}}}`))
|
||||
];
|
||||
if (key) {
|
||||
attrs.push(key);
|
||||
}
|
||||
return t.jSXElement(t.jSXOpeningElement(t.jSXIdentifier('template'), attrs), t.jSXClosingElement(t.jSXIdentifier('template')), []);
|
||||
}
|
||||
exports.buildRefTemplate = buildRefTemplate;
|
||||
function buildJSXAttr(name, value) {
|
||||
return t.jSXAttribute(t.jSXIdentifier(name), t.jSXExpressionContainer(value));
|
||||
}
|
||||
exports.buildJSXAttr = buildJSXAttr;
|
||||
function newJSXIfAttr(jsx, value) {
|
||||
jsx.openingElement.attributes.push(buildJSXAttr(adapter_1.Adapter.if, value));
|
||||
}
|
||||
exports.newJSXIfAttr = newJSXIfAttr;
|
||||
function setJSXAttr(jsx, name, value, path) {
|
||||
const element = jsx.openingElement;
|
||||
if (!t.isJSXIdentifier(element.name)) {
|
||||
return;
|
||||
}
|
||||
if (element.name.name === 'Block' || element.name.name === 'block' || !path) {
|
||||
jsx.openingElement.attributes.push(t.jSXAttribute(t.jSXIdentifier(name), value));
|
||||
}
|
||||
else {
|
||||
const block = buildBlockElement();
|
||||
setJSXAttr(block, name, value);
|
||||
block.children = [jsx];
|
||||
path.node = block;
|
||||
}
|
||||
}
|
||||
exports.setJSXAttr = setJSXAttr;
|
||||
function isAllLiteral(...args) {
|
||||
return args.every(p => t.isLiteral(p));
|
||||
}
|
||||
exports.isAllLiteral = isAllLiteral;
|
||||
function buildBlockElement() {
|
||||
return t.jSXElement(t.jSXOpeningElement(t.jSXIdentifier('block'), []), t.jSXClosingElement(t.jSXIdentifier('block')), []);
|
||||
}
|
||||
exports.buildBlockElement = buildBlockElement;
|
||||
function parseJSXChildren(children) {
|
||||
return children
|
||||
.filter(child => {
|
||||
return !(t.isJSXText(child) && child.value.trim() === '');
|
||||
})
|
||||
.reduce((str, child) => {
|
||||
if (t.isJSXText(child)) {
|
||||
const strings = [];
|
||||
child.value.split(/(\r?\n\s*)/).forEach((val) => {
|
||||
const value = val.replace(/\u00a0/g, ' ').trimLeft();
|
||||
if (!value) {
|
||||
return;
|
||||
}
|
||||
if (value.startsWith('\n')) {
|
||||
return;
|
||||
}
|
||||
strings.push(value);
|
||||
});
|
||||
return str + strings.join('');
|
||||
}
|
||||
if (t.isJSXElement(child)) {
|
||||
return str + parseJSXElement(child);
|
||||
}
|
||||
if (t.isJSXExpressionContainer(child)) {
|
||||
if (t.isJSXElement(child.expression)) {
|
||||
return str + parseJSXElement(child.expression);
|
||||
}
|
||||
return str + `{${utils_1.decodeUnicode(babel_generator_1.default(child, {
|
||||
quotes: 'single',
|
||||
jsonCompatibleStrings: true
|
||||
})
|
||||
.code)
|
||||
.replace(/(this\.props\.)|(this\.data\.)/g, '')
|
||||
.replace(/(props\.)|(data\.)/g, '')
|
||||
.replace(/this\./g, '')
|
||||
.replace(/</g, constant_1.lessThanSignPlacehold)}}`;
|
||||
}
|
||||
return str;
|
||||
}, '');
|
||||
}
|
||||
function parseJSXElement(element) {
|
||||
const children = element.children;
|
||||
const { attributes, name } = element.openingElement;
|
||||
//const TRIGGER_OBSERER = adapter_1.Adapter.type === "swan" /* swan */ ? 'privateTriggerObserer' : '__triggerObserer';
|
||||
if (t.isJSXMemberExpression(name)) {
|
||||
throw utils_1.codeFrameError(name.loc, '暂不支持 JSX 成员表达式');
|
||||
}
|
||||
const componentName = name.name;
|
||||
const isDefaultComponent = constant_1.DEFAULT_Component_SET.has(componentName);
|
||||
const componentSpecialProps = constant_1.SPECIAL_COMPONENT_PROPS.get(componentName);
|
||||
const componentTransfromProps = constant_1.TRANSFORM_COMPONENT_PROPS.get(adapter_1.Adapter.type);
|
||||
let hasElseAttr = false;
|
||||
attributes.forEach((a, index) => {
|
||||
if (a.name.name === adapter_1.Adapter.else && !['block', 'Block'].includes(componentName) && !isDefaultComponent) {
|
||||
hasElseAttr = true;
|
||||
attributes.splice(index, 1);
|
||||
}
|
||||
});
|
||||
if (hasElseAttr) {
|
||||
return create_html_element_1.createHTMLElement({
|
||||
name: 'block',
|
||||
attributes: {
|
||||
[adapter_1.Adapter.else]: true
|
||||
},
|
||||
value: parseJSXChildren([element])
|
||||
});
|
||||
}
|
||||
let attributesTrans = {};
|
||||
if (attributes.length) {
|
||||
attributesTrans = attributes.reduce((obj, attr) => {
|
||||
if (t.isJSXSpreadAttribute(attr)) {
|
||||
throw utils_1.codeFrameError(attr.loc, 'JSX 参数暂不支持 ...spread 表达式');
|
||||
}
|
||||
let name = attr.name.name;
|
||||
if (constant_1.DEFAULT_Component_SET.has(componentName)) {
|
||||
if (name === 'className') {
|
||||
name = 'class';
|
||||
}
|
||||
}
|
||||
let value = true;
|
||||
let attrValue = attr.value;
|
||||
if (typeof name === 'string') {
|
||||
const isAlipayEvent = adapter_1.Adapter.type === "alipay" /* alipay */ && /(^on[A-Z_])|(^catch[A-Z_])/.test(name);
|
||||
if (t.isStringLiteral(attrValue)) {
|
||||
value = attrValue.value;
|
||||
}
|
||||
else if (t.isJSXExpressionContainer(attrValue)) {
|
||||
let isBindEvent = (name.startsWith('bind') && name !== 'bind') || (name.startsWith('catch') && name !== 'catch');
|
||||
let code = utils_1.decodeUnicode(babel_generator_1.default(attrValue.expression, {
|
||||
quotes: 'single',
|
||||
concise: true
|
||||
}).code)
|
||||
.replace(/"/g, "'")
|
||||
.replace(/(this\.props\.)|(this\.data\.)/g, '')
|
||||
.replace(/this\./g, '');
|
||||
if ("swan" /* swan */ === adapter_1.Adapter.type &&
|
||||
code !== 'true' &&
|
||||
code !== 'false' &&
|
||||
constant_1.swanSpecialAttrs[componentName] &&
|
||||
constant_1.swanSpecialAttrs[componentName].includes(name)) {
|
||||
value = `{= ${code} =}`;
|
||||
}
|
||||
else {
|
||||
if (adapter_1.Adapter.key === name) {
|
||||
const splitCode = code.split('.');
|
||||
if (splitCode.length > 1) {
|
||||
value = splitCode.slice(1).join('.');
|
||||
}
|
||||
else {
|
||||
value = code;
|
||||
}
|
||||
}
|
||||
else {
|
||||
value = isBindEvent || isAlipayEvent ? code : `{{${code}}}`;
|
||||
}
|
||||
}
|
||||
if (adapter_1.Adapter.type === "swan" /* swan */ && name === adapter_1.Adapter.for) {
|
||||
value = code;
|
||||
}
|
||||
if (t.isStringLiteral(attrValue.expression)) {
|
||||
value = attrValue.expression.value;
|
||||
}
|
||||
}
|
||||
else if (attrValue === null && name !== adapter_1.Adapter.else) {
|
||||
value = `{{true}}`;
|
||||
}
|
||||
if (constant_1.THIRD_PARTY_COMPONENTS.has(componentName) && /^bind/.test(name) && name.includes('-')) {
|
||||
name = name.replace(/^bind/, 'bind:');
|
||||
}
|
||||
if (componentTransfromProps && componentTransfromProps[componentName]) {
|
||||
const transfromProps = componentTransfromProps[componentName];
|
||||
Object.keys(transfromProps).forEach(oriName => {
|
||||
if (transfromProps.hasOwnProperty(name)) {
|
||||
name = transfromProps[oriName];
|
||||
}
|
||||
});
|
||||
}
|
||||
if ((componentName === 'Input' || componentName === 'input') && name === 'maxLength') {
|
||||
obj['maxlength'] = value;
|
||||
}
|
||||
else if (componentSpecialProps && componentSpecialProps.has(name) ||
|
||||
name.startsWith('__fn_') ||
|
||||
isAlipayEvent) {
|
||||
obj[name] = value;
|
||||
}
|
||||
else {
|
||||
obj[isDefaultComponent && !name.includes('-') && !name.includes(':') ? lodash_1.kebabCase(name) : name] = value;
|
||||
}
|
||||
}
|
||||
if (!isDefaultComponent && !specialComponentName.includes(componentName)) {
|
||||
//obj[TRIGGER_OBSERER] = '{{ _triggerObserer }}';
|
||||
}
|
||||
return obj;
|
||||
}, {});
|
||||
}
|
||||
else if (!isDefaultComponent && !specialComponentName.includes(componentName)) {
|
||||
//attributesTrans[TRIGGER_OBSERER] = '{{ _triggerObserer }}';
|
||||
}
|
||||
return create_html_element_1.createHTMLElement({
|
||||
name: lodash_1.kebabCase(componentName),
|
||||
attributes: attributesTrans,
|
||||
value: parseJSXChildren(children)
|
||||
});
|
||||
}
|
||||
exports.parseJSXElement = parseJSXElement;
|
||||
function generateHTMLTemplate(template, name) {
|
||||
return create_html_element_1.createHTMLElement({
|
||||
name: 'template',
|
||||
attributes: {
|
||||
name
|
||||
},
|
||||
value: parseJSXElement(template)
|
||||
});
|
||||
}
|
||||
exports.generateHTMLTemplate = generateHTMLTemplate;
|
||||
//# sourceMappingURL=jsx.js.map
|
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,33 @@
|
|||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
var Lifecycle;
|
||||
(function (Lifecycle) {
|
||||
Lifecycle["constructor"] = "constructor";
|
||||
Lifecycle["componentWillMount"] = "componentWillMount";
|
||||
Lifecycle["componentDidMount"] = "componentDidMount";
|
||||
Lifecycle["componentWillUpdate"] = "componentWillUpdate";
|
||||
Lifecycle["componentDidUpdate"] = "componentDidUpdate";
|
||||
Lifecycle["componentWillUnmount"] = "componentWillUnmount";
|
||||
Lifecycle["componentDidCatch"] = "componentDidCatch";
|
||||
Lifecycle["componentDidShow"] = "componentDidShow";
|
||||
Lifecycle["componentDidHide"] = "componentDidHide";
|
||||
Lifecycle["componentDidAttached"] = "componentDidAttached";
|
||||
Lifecycle["componentDidMoved"] = "componentDidMoved";
|
||||
Lifecycle["shouldComponentUpdate"] = "shouldComponentUpdate";
|
||||
Lifecycle["componentWillReceiveProps"] = "componentWillReceiveProps";
|
||||
})(Lifecycle = exports.Lifecycle || (exports.Lifecycle = {}));
|
||||
exports.PageLifecycle = {
|
||||
[Lifecycle.componentDidMount]: 'onLaunch',
|
||||
[Lifecycle.componentWillMount]: 'onLoad',
|
||||
[Lifecycle.componentWillUnmount]: 'onUnload',
|
||||
[Lifecycle.componentDidShow]: 'onShow',
|
||||
[Lifecycle.componentDidHide]: 'onHide'
|
||||
};
|
||||
exports.ComponentLifeCycle = {
|
||||
[Lifecycle.componentWillMount]: 'created',
|
||||
[Lifecycle.componentDidAttached]: 'attached',
|
||||
[Lifecycle.componentDidMount]: 'ready',
|
||||
[Lifecycle.componentDidMoved]: 'moved',
|
||||
[Lifecycle.componentWillUnmount]: 'detached'
|
||||
};
|
||||
//# sourceMappingURL=lifecycle.js.map
|
|
@ -0,0 +1 @@
|
|||
{"version":3,"file":"lifecycle.js","sourceRoot":"","sources":["../../src/lifecycle.ts"],"names":[],"mappings":";;AAAA,IAAY,SAcX;AAdD,WAAY,SAAS;IACnB,wCAA2B,CAAA;IAC3B,sDAAyC,CAAA;IACzC,oDAAuC,CAAA;IACvC,wDAA2C,CAAA;IAC3C,sDAAyC,CAAA;IACzC,0DAA6C,CAAA;IAC7C,oDAAuC,CAAA;IACvC,kDAAqC,CAAA;IACrC,kDAAqC,CAAA;IACrC,0DAA6C,CAAA;IAC7C,oDAAuC,CAAA;IACvC,4DAA+C,CAAA;IAC/C,oEAAuD,CAAA;AACzD,CAAC,EAdW,SAAS,GAAT,iBAAS,KAAT,iBAAS,QAcpB;AAEY,QAAA,aAAa,GAAG;IAC3B,CAAC,SAAS,CAAC,iBAAiB,CAAC,EAAE,UAAU;IACzC,CAAC,SAAS,CAAC,kBAAkB,CAAC,EAAE,QAAQ;IACxC,CAAC,SAAS,CAAC,oBAAoB,CAAC,EAAE,UAAU;IAC5C,CAAC,SAAS,CAAC,gBAAgB,CAAC,EAAE,QAAQ;IACtC,CAAC,SAAS,CAAC,gBAAgB,CAAC,EAAE,QAAQ;CACvC,CAAA;AAEY,QAAA,kBAAkB,GAAG;IAChC,CAAC,SAAS,CAAC,kBAAkB,CAAC,EAAE,SAAS;IACzC,CAAC,SAAS,CAAC,oBAAoB,CAAC,EAAE,UAAU;IAC5C,CAAC,SAAS,CAAC,iBAAiB,CAAC,EAAE,OAAO;IACtC,CAAC,SAAS,CAAC,iBAAiB,CAAC,EAAE,OAAO;IACtC,CAAC,SAAS,CAAC,oBAAoB,CAAC,EAAE,UAAU;CAC7C,CAAA"}
|
|
@ -0,0 +1,227 @@
|
|||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const t = require("babel-types");
|
||||
const utils_1 = require("./utils");
|
||||
const jsx_1 = require("./jsx");
|
||||
const constant_1 = require("./constant");
|
||||
const adapter_1 = require("./adapter");
|
||||
// @TODO
|
||||
// 重构 parseRender 和 parseLoop 失败
|
||||
// 尚不清楚 babel 的 data 和 context 传参机制
|
||||
// 目前先写两份代码,有时间看看 babel 具体对 data 和 context 做了什么导致传参失败
|
||||
function parseLoopBody(body, jsxDeclarations,
|
||||
// @TODO
|
||||
// 把 templates 换成 Map 可以支持 shalow variables declared
|
||||
// 现在先用 ESLint 的 no-shalow 顶着
|
||||
templates, loopScopes, finalReturnElement, returnedPaths) {
|
||||
const bodyScope = body.scope;
|
||||
body.traverse({
|
||||
JSXElement(jsxElementPath) {
|
||||
const parentNode = jsxElementPath.parent;
|
||||
const parentPath = jsxElementPath.parentPath;
|
||||
const isFinalReturn = jsxElementPath.getFunctionParent().isClassMethod();
|
||||
const isJSXChildren = t.isJSXElement(parentNode);
|
||||
if (!isJSXChildren) {
|
||||
let statementParent = jsxElementPath.getStatementParent();
|
||||
if (!(statementParent.isVariableDeclaration() ||
|
||||
statementParent.isExpressionStatement())) {
|
||||
statementParent = statementParent.findParent(s => s.isVariableDeclaration() || s.isExpressionStatement());
|
||||
}
|
||||
jsxDeclarations.add(statementParent);
|
||||
if (t.isVariableDeclarator(parentNode)) {
|
||||
if (statementParent) {
|
||||
const name = utils_1.findIdentifierFromStatement(statementParent.node);
|
||||
// setTemplate(name, path, templates)
|
||||
name && templates.set(name, jsxElementPath.node);
|
||||
}
|
||||
}
|
||||
else if (t.isLogicalExpression(parentNode)) {
|
||||
const { left, operator } = parentNode;
|
||||
if (operator === '&&') {
|
||||
if (t.isExpression(left)) {
|
||||
utils_1.newJSXIfAttr(jsxElementPath.node, left);
|
||||
parentPath.replaceWith(jsxElementPath.node);
|
||||
if (statementParent) {
|
||||
const name = utils_1.findIdentifierFromStatement(statementParent.node);
|
||||
utils_1.setTemplate(name, jsxElementPath, templates);
|
||||
// name && templates.set(name, path.node)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (t.isConditionalExpression(parentNode)) {
|
||||
const { test, consequent, alternate } = parentNode;
|
||||
const block = jsx_1.buildBlockElement();
|
||||
if (t.isJSXElement(consequent) && t.isLiteral(alternate)) {
|
||||
const { value, confident } = parentPath.get('alternate').evaluate();
|
||||
if (confident && !value) {
|
||||
utils_1.newJSXIfAttr(block, test);
|
||||
block.children = [jsxElementPath.node];
|
||||
// newJSXIfAttr(jsxElementPath.node, test)
|
||||
parentPath.replaceWith(block);
|
||||
if (statementParent) {
|
||||
const name = utils_1.findIdentifierFromStatement(statementParent.node);
|
||||
utils_1.setTemplate(name, jsxElementPath, templates);
|
||||
// name && templates.set(name, path.node)
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (t.isLiteral(consequent) && t.isJSXElement(consequent)) {
|
||||
if (t.isNullLiteral(consequent)) {
|
||||
utils_1.newJSXIfAttr(block, utils_1.reverseBoolean(test));
|
||||
// newJSXIfAttr(jsxElementPath.node, reverseBoolean(test))
|
||||
parentPath.replaceWith(block);
|
||||
if (statementParent) {
|
||||
const name = utils_1.findIdentifierFromStatement(statementParent.node);
|
||||
utils_1.setTemplate(name, jsxElementPath, templates);
|
||||
// name && templates.set(name, path.node)
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (t.isJSXElement(consequent) && t.isJSXElement(alternate)) {
|
||||
const block2 = jsx_1.buildBlockElement();
|
||||
block.children = [consequent];
|
||||
utils_1.newJSXIfAttr(block, test);
|
||||
jsx_1.setJSXAttr(block2, adapter_1.Adapter.else);
|
||||
block2.children = [alternate];
|
||||
const parentBlock = jsx_1.buildBlockElement();
|
||||
parentBlock.children = [block, block2];
|
||||
parentPath.replaceWith(parentBlock);
|
||||
if (statementParent) {
|
||||
const name = utils_1.findIdentifierFromStatement(statementParent.node);
|
||||
utils_1.setTemplate(name, jsxElementPath, templates);
|
||||
}
|
||||
}
|
||||
else {
|
||||
// console.log('todo')
|
||||
}
|
||||
}
|
||||
else if (t.isReturnStatement(parentNode)) {
|
||||
if (!isFinalReturn) {
|
||||
const caller = parentPath.findParent(p => p.isCallExpression());
|
||||
if (caller.isCallExpression()) {
|
||||
const callee = caller.node.callee;
|
||||
if (t.isMemberExpression(callee) &&
|
||||
t.isIdentifier(callee.property) &&
|
||||
callee.property.name === 'map') {
|
||||
let ary = callee.object;
|
||||
const blockStatementPath = parentPath.findParent(p => p.isBlockStatement());
|
||||
const body = blockStatementPath.node.body;
|
||||
let stateToBeAssign = new Set();
|
||||
for (const statement of body) {
|
||||
if (t.isVariableDeclaration(statement)) {
|
||||
for (const dcl of statement.declarations) {
|
||||
if (t.isIdentifier(dcl.id)) {
|
||||
const scope = blockStatementPath.scope;
|
||||
const stateName = scope.generateUid(constant_1.LOOP_STATE);
|
||||
stateToBeAssign.add(stateName);
|
||||
blockStatementPath.scope.rename(dcl.id.name, stateName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (t.isCallExpression(ary) || utils_1.isContainFunction(caller.get('callee').get('object'))) {
|
||||
const variableName = `anonymousState_${bodyScope.generateUid()}`;
|
||||
caller.getStatementParent().insertBefore(utils_1.buildConstVariableDeclaration(variableName, ary));
|
||||
ary = t.identifier(variableName);
|
||||
}
|
||||
jsx_1.setJSXAttr(jsxElementPath.node, adapter_1.Adapter.for, t.jSXExpressionContainer(ary));
|
||||
const [func] = caller.node.arguments;
|
||||
if (t.isFunctionExpression(func) ||
|
||||
t.isArrowFunctionExpression(func)) {
|
||||
const [item, index] = func.params;
|
||||
if (t.isIdentifier(item)) {
|
||||
jsx_1.setJSXAttr(jsxElementPath.node, adapter_1.Adapter.forItem, t.stringLiteral(item.name));
|
||||
loopScopes.add(item.name);
|
||||
}
|
||||
else {
|
||||
jsx_1.setJSXAttr(jsxElementPath.node, adapter_1.Adapter.forItem, t.stringLiteral('__item'));
|
||||
}
|
||||
if (t.isIdentifier(index)) {
|
||||
jsx_1.setJSXAttr(jsxElementPath.node, adapter_1.Adapter.forIndex, t.stringLiteral(index.name));
|
||||
loopScopes.add(index.name);
|
||||
}
|
||||
caller.replaceWith(jsxElementPath.node);
|
||||
if (statementParent) {
|
||||
const name = utils_1.findIdentifierFromStatement(statementParent.node);
|
||||
// setTemplate(name, path, templates)
|
||||
name && templates.set(name, jsxElementPath.node);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
const ifStatement = parentPath.findParent(p => p.isIfStatement());
|
||||
const blockStatement = parentPath.findParent(p => p.isBlockStatement());
|
||||
const block = finalReturnElement || jsx_1.buildBlockElement();
|
||||
if (utils_1.isBlockIfStatement(ifStatement, blockStatement)) {
|
||||
const { test, alternate, consequent } = ifStatement.node;
|
||||
if (alternate === blockStatement.node) {
|
||||
throw utils_1.codeFrameError(parentNode.loc, '不必要的 else 分支,请遵从 ESLint consistent-return: https://eslint.org/docs/rules/consistent-return');
|
||||
}
|
||||
else if (consequent === blockStatement.node) {
|
||||
const parentIfStatement = ifStatement.findParent(p => p.isIfStatement());
|
||||
if (parentIfStatement) {
|
||||
jsx_1.setJSXAttr(jsxElementPath.node, adapter_1.Adapter.elseif, t.jSXExpressionContainer(test));
|
||||
}
|
||||
else {
|
||||
utils_1.newJSXIfAttr(jsxElementPath.node, test);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (block.children.length !== 0) {
|
||||
jsx_1.setJSXAttr(jsxElementPath.node, adapter_1.Adapter.else);
|
||||
}
|
||||
block.children.push(jsxElementPath.node);
|
||||
finalReturnElement = block;
|
||||
returnedPaths.push(parentPath);
|
||||
}
|
||||
}
|
||||
else if (t.isArrowFunctionExpression(parentNode)) {
|
||||
//
|
||||
}
|
||||
else if (t.isAssignmentExpression(parentNode)) {
|
||||
if (t.isIdentifier(parentNode.left)) {
|
||||
const name = parentNode.left.name;
|
||||
const bindingNode = bodyScope.getOwnBinding(name).path.node;
|
||||
const block = templates.get(name) || jsx_1.buildBlockElement();
|
||||
if (utils_1.isEmptyDeclarator(bindingNode)) {
|
||||
const ifStatement = parentPath.findParent(p => p.isIfStatement());
|
||||
const blockStatement = parentPath.findParent(p => p.isBlockStatement());
|
||||
if (utils_1.isBlockIfStatement(ifStatement, blockStatement)) {
|
||||
const { test, alternate, consequent } = ifStatement.node;
|
||||
if (alternate === blockStatement.node) {
|
||||
jsx_1.setJSXAttr(jsxElementPath.node, adapter_1.Adapter.else);
|
||||
}
|
||||
else if (consequent === blockStatement.node) {
|
||||
const parentIfStatement = ifStatement.findParent(p => p.isIfStatement());
|
||||
if (parentIfStatement && parentIfStatement.get('alternate') === ifStatement) {
|
||||
jsx_1.setJSXAttr(jsxElementPath.node, adapter_1.Adapter.elseif, t.jSXExpressionContainer(test));
|
||||
}
|
||||
else {
|
||||
if (parentIfStatement) {
|
||||
utils_1.newJSXIfAttr(block, parentIfStatement.node.test);
|
||||
}
|
||||
utils_1.newJSXIfAttr(jsxElementPath.node, test);
|
||||
}
|
||||
}
|
||||
block.children.push(jsxElementPath.node);
|
||||
// setTemplate(name, path, templates)
|
||||
name && templates.set(name, block);
|
||||
}
|
||||
}
|
||||
else {
|
||||
throw utils_1.codeFrameError(jsxElementPath.node.loc, '请将 JSX 赋值表达式初始化为 null,然后再进行 if 条件表达式赋值。');
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (!t.isJSXElement(parentNode)) {
|
||||
// throwError(path, '考虑只对 JSX 元素赋值一次。')
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
exports.parseLoopBody = parseLoopBody;
|
||||
//# sourceMappingURL=loop-component.js.map
|
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,37 @@
|
|||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const eslint_1 = require("./eslint");
|
||||
exports.transformOptions = {};
|
||||
exports.setTransformOptions = (options) => {
|
||||
for (const key in options) {
|
||||
if (options.hasOwnProperty(key)) {
|
||||
exports.transformOptions[key] = options[key];
|
||||
}
|
||||
}
|
||||
};
|
||||
exports.buildBabelTransformOptions = () => {
|
||||
return {
|
||||
parserOpts: {
|
||||
sourceType: 'module',
|
||||
plugins: [
|
||||
'classProperties',
|
||||
'jsx',
|
||||
'flow',
|
||||
'flowComment',
|
||||
'trailingFunctionCommas',
|
||||
'asyncFunctions',
|
||||
'exponentiationOperator',
|
||||
'asyncGenerators',
|
||||
'objectRestSpread',
|
||||
'decorators',
|
||||
'dynamicImport'
|
||||
]
|
||||
},
|
||||
plugins: [
|
||||
require('babel-plugin-transform-flow-strip-types'),
|
||||
[require('babel-plugin-transform-define').default, exports.transformOptions.env]
|
||||
].concat(process.env.ESLINT === 'false' || exports.transformOptions.isNormal || exports.transformOptions.isTyped ? [] : eslint_1.eslintValidation)
|
||||
.concat((process.env.NODE_ENV === 'test') ? [] : require('babel-plugin-remove-dead-code').default)
|
||||
};
|
||||
};
|
||||
//# sourceMappingURL=options.js.map
|
|
@ -0,0 +1 @@
|
|||
{"version":3,"file":"options.js","sourceRoot":"","sources":["../../src/options.ts"],"names":[],"mappings":";;AACA,qCAA2C;AAe9B,QAAA,gBAAgB,GAAY,EAAS,CAAA;AAErC,QAAA,mBAAmB,GAAG,CAAC,OAAgB,EAAE,EAAE;IACtD,KAAK,MAAM,GAAG,IAAI,OAAO,EAAE;QACzB,IAAI,OAAO,CAAC,cAAc,CAAC,GAAG,CAAC,EAAE;YAC/B,wBAAgB,CAAC,GAAG,CAAC,GAAG,OAAO,CAAC,GAAG,CAAC,CAAA;SACrC;KACF;AACH,CAAC,CAAA;AAEY,QAAA,0BAA0B,GAA2B,GAAG,EAAE;IACrE,OAAO;QACL,UAAU,EAAE;YACV,UAAU,EAAE,QAAQ;YACpB,OAAO,EAAE;gBACP,iBAAiB;gBACjB,KAAK;gBACL,MAAM;gBACN,aAAa;gBACb,wBAAwB;gBACxB,gBAAgB;gBAChB,wBAAwB;gBACxB,iBAAiB;gBACjB,kBAAkB;gBAClB,YAAY;gBACZ,eAAe;aACP;SACX;QACD,OAAO,EAAE;YACP,OAAO,CAAC,yCAAyC,CAAC;YAClD,CAAC,OAAO,CAAC,+BAA+B,CAAC,CAAC,OAAO,EAAE,wBAAgB,CAAC,GAAG,CAAC;SACzE,CAAC,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,KAAK,OAAO,IAAI,wBAAgB,CAAC,QAAQ,IAAI,wBAAgB,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,yBAAgB,CAAC;aACxH,MAAM,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,+BAA+B,CAAC,CAAC,OAAO,CAAC;KACnG,CAAA;AACH,CAAC,CAAA"}
|
|
@ -0,0 +1,45 @@
|
|||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const t = require("babel-types");
|
||||
function isString(node) {
|
||||
return t.isLiteral(node) && typeof node.value === 'string';
|
||||
}
|
||||
function buildBinaryExpression(left, right) {
|
||||
return t.binaryExpression('+', left, right);
|
||||
}
|
||||
function templateLiterals(path, state) {
|
||||
let nodes = [];
|
||||
const expressions = path.get('expressions');
|
||||
for (const elem of (path.node.quasis)) {
|
||||
nodes.push(t.stringLiteral(elem.value.cooked));
|
||||
const expr = expressions.shift();
|
||||
if (expr) {
|
||||
// tslint:disable-next-line:no-multi-spaces
|
||||
if (state.opts.spec && !expr.isBaseType('string') && !expr.isBaseType('number')) {
|
||||
nodes.push(t.callExpression(t.identifier('String'), [expr.node]));
|
||||
}
|
||||
else {
|
||||
nodes.push(expr.node);
|
||||
}
|
||||
}
|
||||
}
|
||||
// filter out empty string literals
|
||||
nodes = nodes.filter((n) => !t.isLiteral(n, { value: '' }));
|
||||
// since `+` is left-to-right associative
|
||||
// ensure the first node is a string if first/second isn't
|
||||
if (!isString(nodes[0]) && !isString(nodes[1])) {
|
||||
nodes.unshift(t.stringLiteral(''));
|
||||
}
|
||||
if (nodes.length > 1) {
|
||||
let root = buildBinaryExpression(nodes.shift(), nodes.shift());
|
||||
for (const node of nodes) {
|
||||
root = buildBinaryExpression(root, node);
|
||||
}
|
||||
path.replaceWith(root);
|
||||
}
|
||||
else {
|
||||
path.replaceWith(nodes[0]);
|
||||
}
|
||||
}
|
||||
exports.templateLiterals = templateLiterals;
|
||||
//# sourceMappingURL=plugins.js.map
|
|
@ -0,0 +1 @@
|
|||
{"version":3,"file":"plugins.js","sourceRoot":"","sources":["../../src/plugins.ts"],"names":[],"mappings":";;AAAA,iCAAgC;AAEhC,SAAS,QAAQ,CAAE,IAAI;IACrB,OAAO,CAAC,CAAC,SAAS,CAAC,IAAW,CAAC,IAAI,OAAO,IAAI,CAAC,KAAK,KAAK,QAAQ,CAAA;AACnE,CAAC;AAED,SAAS,qBAAqB,CAAE,IAAI,EAAE,KAAK;IACzC,OAAO,CAAC,CAAC,gBAAgB,CAAC,GAAG,EAAE,IAAI,EAAE,KAAK,CAAC,CAAA;AAC7C,CAAC;AACD,SAAgB,gBAAgB,CAAE,IAAI,EAAE,KAAK;IAE3C,IAAI,KAAK,GAAkB,EAAE,CAAA;IAE7B,MAAM,WAAW,GAAG,IAAI,CAAC,GAAG,CAAC,aAAa,CAAC,CAAA;IAE3C,KAAK,MAAM,IAAI,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE;QACrC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAA;QAE9C,MAAM,IAAI,GAAG,WAAW,CAAC,KAAK,EAAE,CAAA;QAChC,IAAI,IAAI,EAAE;YACR,2CAA2C;YAC3C,IAAI,KAAK,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAG;gBAChF,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;aAClE;iBAAM;gBACL,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;aACtB;SACF;KACF;IAED,mCAAmC;IACnC,KAAK,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC,CAAC,CAAA;IAE3D,yCAAyC;IACzC,0DAA0D;IAC1D,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE;QAC9C,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,aAAa,CAAC,EAAE,CAAC,CAAC,CAAA;KACnC;IAED,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE;QACpB,IAAI,IAAI,GAAG,qBAAqB,CAAC,KAAK,CAAC,KAAK,EAAE,EAAE,KAAK,CAAC,KAAK,EAAE,CAAC,CAAA;QAE9D,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE;YACxB,IAAI,GAAG,qBAAqB,CAAC,IAAI,EAAE,IAAI,CAAC,CAAA;SACzC;QAED,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,CAAA;KACvB;SAAM;QACL,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAA;KAC3B;AACH,CAAC;AAxCD,4CAwCC"}
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,493 @@
|
|||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const t = require("babel-types");
|
||||
const babel_generator_1 = require("babel-generator");
|
||||
const code_frame_1 = require("@babel/code-frame");
|
||||
const constant_1 = require("./constant");
|
||||
const lodash_1 = require("lodash");
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
const jsx_1 = require("./jsx");
|
||||
const adapter_1 = require("./adapter");
|
||||
const options_1 = require("./options");
|
||||
const template = require('babel-template');
|
||||
exports.incrementId = () => {
|
||||
let id = 0;
|
||||
return () => id++;
|
||||
};
|
||||
function getSuperClassCode(path) {
|
||||
const superClass = path.node.superClass;
|
||||
if (t.isIdentifier(superClass)) {
|
||||
const binding = path.scope.getBinding(superClass.name);
|
||||
if (binding && binding.kind === 'module') {
|
||||
const bindingPath = binding.path.parentPath;
|
||||
if (bindingPath.isImportDeclaration()) {
|
||||
const source = bindingPath.node.source;
|
||||
if (source.value === constant_1.TARO_PACKAGE_NAME) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const p = pathResolver(source.value, options_1.transformOptions.sourcePath) + (options_1.transformOptions.isTyped ? '.tsx' : '.js');
|
||||
const code = fs.readFileSync(p, 'utf8');
|
||||
return {
|
||||
code,
|
||||
sourcePath: source.value
|
||||
};
|
||||
}
|
||||
catch (error) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
exports.getSuperClassCode = getSuperClassCode;
|
||||
function isContainStopPropagation(path) {
|
||||
let matched = false;
|
||||
if (path) {
|
||||
path.traverse({
|
||||
Identifier(p) {
|
||||
if (p.node.name === 'stopPropagation' &&
|
||||
p.parentPath.parentPath.isCallExpression()) {
|
||||
matched = true;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
return matched;
|
||||
}
|
||||
exports.isContainStopPropagation = isContainStopPropagation;
|
||||
function decodeUnicode(s) {
|
||||
return unescape(s.replace(/\\(u[0-9a-fA-F]{4})/gm, '%$1'));
|
||||
}
|
||||
exports.decodeUnicode = decodeUnicode;
|
||||
function isVarName(str) {
|
||||
if (typeof str !== 'string') {
|
||||
return false;
|
||||
}
|
||||
if (str.trim() !== str) {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
// tslint:disable-next-line:no-unused-expression
|
||||
new Function(str, 'var ' + str);
|
||||
}
|
||||
catch (e) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
exports.isVarName = isVarName;
|
||||
function findMethodName(expression) {
|
||||
let methodName;
|
||||
if (t.isIdentifier(expression) ||
|
||||
t.isJSXIdentifier(expression)) {
|
||||
methodName = expression.name;
|
||||
}
|
||||
else if (t.isStringLiteral(expression)) {
|
||||
methodName = expression.value;
|
||||
}
|
||||
else if (t.isMemberExpression(expression) &&
|
||||
t.isIdentifier(expression.property)) {
|
||||
const { code } = babel_generator_1.default(expression);
|
||||
const ids = code.split('.');
|
||||
if (ids[0] === 'this' && ids[1] === 'props' && ids[2]) {
|
||||
methodName = code.replace('this.props.', '');
|
||||
}
|
||||
else {
|
||||
methodName = expression.property.name;
|
||||
}
|
||||
}
|
||||
else if (t.isCallExpression(expression) &&
|
||||
t.isMemberExpression(expression.callee) &&
|
||||
t.isIdentifier(expression.callee.object)) {
|
||||
methodName = expression.callee.object.name;
|
||||
}
|
||||
else if (t.isCallExpression(expression) &&
|
||||
t.isMemberExpression(expression.callee) &&
|
||||
t.isMemberExpression(expression.callee.object) &&
|
||||
t.isIdentifier(expression.callee.property) &&
|
||||
expression.callee.property.name === 'bind' &&
|
||||
t.isIdentifier(expression.callee.object.property)) {
|
||||
methodName = expression.callee.object.property.name;
|
||||
}
|
||||
else {
|
||||
throw codeFrameError(expression.loc, '当 props 为事件时(props name 以 `on` 开头),只能传入一个 this 作用域下的函数。');
|
||||
}
|
||||
return methodName;
|
||||
}
|
||||
exports.findMethodName = findMethodName;
|
||||
function setParentCondition(jsx, expr, array = false) {
|
||||
const conditionExpr = jsx.findParent(p => p.isConditionalExpression());
|
||||
const logicExpr = jsx.findParent(p => p.isLogicalExpression({ operator: '&&' }));
|
||||
if (array) {
|
||||
const ifAttrSet = new Set([
|
||||
adapter_1.Adapter.if,
|
||||
adapter_1.Adapter.else
|
||||
]);
|
||||
const logicalJSX = jsx.findParent(p => p.isJSXElement() && p.node.openingElement.attributes.some(a => ifAttrSet.has(a.name.name)));
|
||||
if (logicalJSX) {
|
||||
const attr = logicalJSX.node.openingElement.attributes.find(a => ifAttrSet.has(a.name.name));
|
||||
if (attr) {
|
||||
if (attr.name.name === adapter_1.Adapter.else) {
|
||||
const prevElement = logicalJSX.getPrevSibling();
|
||||
if (prevElement && prevElement.isJSXElement()) {
|
||||
const attr = prevElement.node.openingElement.attributes.find(a => a.name.name === adapter_1.Adapter.if);
|
||||
if (attr && t.isJSXExpressionContainer(attr.value)) {
|
||||
expr = t.conditionalExpression(reverseBoolean(lodash_1.cloneDeep(attr.value.expression)), expr, t.arrayExpression());
|
||||
return expr;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (t.isJSXExpressionContainer(attr.value)) {
|
||||
expr = t.conditionalExpression(lodash_1.cloneDeep(attr.value.expression), expr, t.arrayExpression());
|
||||
return expr;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (conditionExpr && conditionExpr.isConditionalExpression()) {
|
||||
const consequent = conditionExpr.get('consequent');
|
||||
if (consequent === jsx || jsx.findParent(p => p === consequent)) {
|
||||
expr = t.conditionalExpression(lodash_1.cloneDeep(conditionExpr.get('test').node), expr, array ? t.arrayExpression([]) : t.nullLiteral());
|
||||
}
|
||||
}
|
||||
if (logicExpr && logicExpr.isLogicalExpression({ operator: '&&' })) {
|
||||
const consequent = logicExpr.get('right');
|
||||
if (consequent === jsx || jsx.findParent(p => p === consequent)) {
|
||||
expr = t.conditionalExpression(lodash_1.cloneDeep(logicExpr.get('left').node), expr, array ? t.arrayExpression([]) : t.nullLiteral());
|
||||
}
|
||||
}
|
||||
return expr;
|
||||
}
|
||||
exports.setParentCondition = setParentCondition;
|
||||
function generateAnonymousState(scope, expression, refIds, isLogical) {
|
||||
let variableName = `anonymousState_${scope.generateUid()}`;
|
||||
let statementParent = expression.getStatementParent();
|
||||
if (!statementParent) {
|
||||
throw codeFrameError(expression.node.loc, '无法生成匿名 State,尝试先把值赋到一个变量上再把变量调换。');
|
||||
}
|
||||
const jsx = isLogical ? expression : expression.findParent(p => p.isJSXElement());
|
||||
const callExpr = jsx.findParent(p => p.isCallExpression() && isArrayMapCallExpression(p));
|
||||
const ifExpr = jsx.findParent(p => p.isIfStatement());
|
||||
const blockStatement = jsx.findParent(p => p.isBlockStatement() && p.parentPath === ifExpr);
|
||||
const expr = setParentCondition(jsx, lodash_1.cloneDeep(expression.node));
|
||||
if (!callExpr) {
|
||||
refIds.add(t.identifier(variableName));
|
||||
statementParent.insertBefore(buildConstVariableDeclaration(variableName, expr));
|
||||
if (blockStatement && blockStatement.isBlockStatement()) {
|
||||
blockStatement.traverse({
|
||||
VariableDeclarator: (p) => {
|
||||
const { id, init } = p.node;
|
||||
if (t.isIdentifier(id) && !id.name.startsWith(constant_1.LOOP_STATE)) {
|
||||
const newId = scope.generateDeclaredUidIdentifier('$' + id.name);
|
||||
refIds.forEach((refId) => {
|
||||
if (refId.name === variableName && !variableName.startsWith('_$')) {
|
||||
refIds.delete(refId);
|
||||
}
|
||||
});
|
||||
variableName = newId.name;
|
||||
refIds.add(t.identifier(variableName));
|
||||
blockStatement.scope.rename(id.name, newId.name);
|
||||
p.parentPath.replaceWith(template('ID = INIT;')({ ID: newId, INIT: init }));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
else {
|
||||
variableName = `${constant_1.LOOP_STATE}_${callExpr.scope.generateUid()}`;
|
||||
const func = callExpr.node.arguments[0];
|
||||
if (t.isArrowFunctionExpression(func)) {
|
||||
if (!t.isBlockStatement(func.body)) {
|
||||
func.body = t.blockStatement([
|
||||
buildConstVariableDeclaration(variableName, expr),
|
||||
t.returnStatement(func.body)
|
||||
]);
|
||||
}
|
||||
else {
|
||||
func.body.body.splice(func.body.body.length - 1, 0, buildConstVariableDeclaration(variableName, expr));
|
||||
}
|
||||
}
|
||||
}
|
||||
const id = t.identifier(variableName);
|
||||
expression.replaceWith(id);
|
||||
return id;
|
||||
}
|
||||
exports.generateAnonymousState = generateAnonymousState;
|
||||
function isArrayMapCallExpression(callExpression) {
|
||||
return callExpression &&
|
||||
t.isCallExpression(callExpression.node) &&
|
||||
t.isMemberExpression(callExpression.node.callee) &&
|
||||
t.isIdentifier(callExpression.node.callee.property, { name: 'map' });
|
||||
}
|
||||
exports.isArrayMapCallExpression = isArrayMapCallExpression;
|
||||
function buildConstVariableDeclaration(variableName, expresion) {
|
||||
return t.variableDeclaration('const', [
|
||||
t.variableDeclarator(t.identifier(variableName), expresion)
|
||||
]);
|
||||
}
|
||||
exports.buildConstVariableDeclaration = buildConstVariableDeclaration;
|
||||
function setTemplate(name, path, templates) {
|
||||
const parentPath = path.parentPath;
|
||||
const jsxChildren = parentPath.findParent(p => p.isJSXElement());
|
||||
if (name && !jsxChildren) {
|
||||
templates.set(name, path.node);
|
||||
}
|
||||
}
|
||||
exports.setTemplate = setTemplate;
|
||||
function isContainFunction(p) {
|
||||
let bool = false;
|
||||
p.traverse({
|
||||
CallExpression() {
|
||||
bool = true;
|
||||
}
|
||||
});
|
||||
return bool;
|
||||
}
|
||||
exports.isContainFunction = isContainFunction;
|
||||
function slash(input) {
|
||||
const isExtendedLengthPath = /^\\\\\?\\/.test(input);
|
||||
const hasNonAscii = /[^\u0000-\u0080]+/.test(input);
|
||||
const hasChinese = /[^\u4e00-\u9fa5]+/.test(input); // has Chinese characters
|
||||
if (isExtendedLengthPath || (hasNonAscii && !hasChinese)) {
|
||||
return input;
|
||||
}
|
||||
return input.replace(/\\/g, '/');
|
||||
}
|
||||
function pathResolver(source, location) {
|
||||
const extName = path.extname(source);
|
||||
const promotedPath = source;
|
||||
if (!['js', 'tsx'].includes(extName)) {
|
||||
try {
|
||||
const pathExist = fs.existsSync(path.resolve(path.dirname(location), source, 'index.js'));
|
||||
const tsxPathExist = fs.existsSync(path.resolve(path.dirname(location), source, 'index.tsx'));
|
||||
if (pathExist || tsxPathExist) {
|
||||
let p = path.join(promotedPath, 'index');
|
||||
if (!p.startsWith('.')) {
|
||||
p = './' + p;
|
||||
}
|
||||
return slash(p);
|
||||
}
|
||||
return slash(promotedPath);
|
||||
}
|
||||
catch (error) {
|
||||
return slash(promotedPath);
|
||||
}
|
||||
}
|
||||
return slash(promotedPath.split('.').slice(0, -1).join('.'));
|
||||
}
|
||||
exports.pathResolver = pathResolver;
|
||||
function codeFrameError(node, msg) {
|
||||
let errMsg = '';
|
||||
try {
|
||||
errMsg = code_frame_1.codeFrameColumns(exports.setting.sourceCode, node && node.type && node.loc ? node.loc : node, {
|
||||
highlightCode: true
|
||||
});
|
||||
}
|
||||
catch (error) {
|
||||
errMsg = 'failed to locate source';
|
||||
}
|
||||
return new Error(`${msg}
|
||||
-----
|
||||
${errMsg}`);
|
||||
}
|
||||
exports.codeFrameError = codeFrameError;
|
||||
exports.setting = {
|
||||
sourceCode: ''
|
||||
};
|
||||
function createUUID() {
|
||||
return '$' + 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
|
||||
let r = Math.random() * 16 | 0;
|
||||
let v = c === 'x' ? r : (r & 0x3 | 0x8);
|
||||
return v.toString(16);
|
||||
}).replace(/-/g, '').slice(0, 8);
|
||||
}
|
||||
exports.createUUID = createUUID;
|
||||
function createRandomLetters(n) {
|
||||
const str = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
|
||||
return Array(n).join().split(',').map(function () { return str.charAt(Math.floor(Math.random() * str.length)); }).join('');
|
||||
}
|
||||
exports.createRandomLetters = createRandomLetters;
|
||||
function isBlockIfStatement(ifStatement, blockStatement) {
|
||||
return ifStatement && blockStatement &&
|
||||
ifStatement.isIfStatement() &&
|
||||
blockStatement.isBlockStatement();
|
||||
}
|
||||
exports.isBlockIfStatement = isBlockIfStatement;
|
||||
function buildCodeFrame(code) {
|
||||
return (loc) => code_frame_1.codeFrameColumns(code, loc);
|
||||
}
|
||||
exports.buildCodeFrame = buildCodeFrame;
|
||||
function isNumeric(n) {
|
||||
return !isNaN(parseFloat(n)) && isFinite(n);
|
||||
}
|
||||
exports.isNumeric = isNumeric;
|
||||
function buildJSXAttr(name, value) {
|
||||
return t.jSXAttribute(t.jSXIdentifier(name), t.jSXExpressionContainer(value));
|
||||
}
|
||||
exports.buildJSXAttr = buildJSXAttr;
|
||||
function newJSXIfAttr(jsx, value, path) {
|
||||
const element = jsx.openingElement;
|
||||
if (!t.isJSXIdentifier(element.name)) {
|
||||
return;
|
||||
}
|
||||
if (element.name.name === 'Block' || element.name.name === 'block' || !path) {
|
||||
element.attributes.push(buildJSXAttr(adapter_1.Adapter.if, value));
|
||||
}
|
||||
else {
|
||||
const block = jsx_1.buildBlockElement();
|
||||
newJSXIfAttr(block, value);
|
||||
block.children.push(jsx);
|
||||
path.node = block;
|
||||
}
|
||||
}
|
||||
exports.newJSXIfAttr = newJSXIfAttr;
|
||||
function getSlotName(name) {
|
||||
return name.slice(6).toLowerCase();
|
||||
}
|
||||
exports.getSlotName = getSlotName;
|
||||
function isContainJSXElement(path) {
|
||||
let matched = false;
|
||||
path.traverse({
|
||||
JSXElement(p) {
|
||||
matched = true;
|
||||
p.stop();
|
||||
}
|
||||
});
|
||||
return matched;
|
||||
}
|
||||
exports.isContainJSXElement = isContainJSXElement;
|
||||
function hasComplexExpression(path) {
|
||||
let matched = false;
|
||||
if (isContainJSXElement(path)) {
|
||||
return false;
|
||||
}
|
||||
if (path.isObjectExpression()) {
|
||||
return true;
|
||||
}
|
||||
if (path.isTemplateLiteral() || path.isCallExpression()) {
|
||||
return true;
|
||||
}
|
||||
if (path.isArrayExpression()) {
|
||||
const { elements } = path.node;
|
||||
if (elements.some(el => t.isObjectExpression(el) || t.isArrayExpression(el))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
path.traverse({
|
||||
CallExpression: (p) => {
|
||||
matched = true;
|
||||
p.stop();
|
||||
},
|
||||
TemplateLiteral(p) {
|
||||
matched = true;
|
||||
p.stop();
|
||||
},
|
||||
ObjectExpression(p) {
|
||||
matched = true;
|
||||
p.stop();
|
||||
},
|
||||
ArrayExpression(p) {
|
||||
const { elements } = p.node;
|
||||
if (elements.some(el => t.isObjectExpression(el))) {
|
||||
return true;
|
||||
}
|
||||
},
|
||||
TaggedTemplateExpression(p) {
|
||||
matched = true;
|
||||
p.stop();
|
||||
},
|
||||
MemberExpression(path) {
|
||||
// @fix 放弃这个逻辑,导致后续变更 this.update 的 data 对应不上了
|
||||
// const jsxElement = path.findParent(p => p.isJSXExpressionContainer());
|
||||
// const object = path.get('object');
|
||||
// const property = path.get('property');
|
||||
// const parentPath = path.parentPath;
|
||||
// if (jsxElement &&
|
||||
// object.isThisExpression() &&
|
||||
// property.isIdentifier({ name: 'data' }) &&
|
||||
// parentPath.isMemberExpression() &&
|
||||
// parentPath.parentPath.isMemberExpression()) {
|
||||
// const sourceCode = parentPath.parentPath.getSource();
|
||||
// if (sourceCode.includes('[') && sourceCode.includes(']')) {
|
||||
// matched = true;
|
||||
// path.stop();
|
||||
// }
|
||||
// }
|
||||
}
|
||||
});
|
||||
return matched;
|
||||
}
|
||||
exports.hasComplexExpression = hasComplexExpression;
|
||||
function findFirstIdentifierFromMemberExpression(node, member) {
|
||||
let id;
|
||||
let object = node.object;
|
||||
while (true) {
|
||||
if (t.identifier(object) && !t.isMemberExpression(object)) {
|
||||
id = object;
|
||||
if (member) {
|
||||
object = member;
|
||||
}
|
||||
break;
|
||||
}
|
||||
object = object.object;
|
||||
}
|
||||
return id;
|
||||
}
|
||||
exports.findFirstIdentifierFromMemberExpression = findFirstIdentifierFromMemberExpression;
|
||||
function getArgumentName(arg) {
|
||||
if (t.isThisExpression(arg)) {
|
||||
return 'this';
|
||||
}
|
||||
else if (t.isNullLiteral(arg)) {
|
||||
return 'null';
|
||||
}
|
||||
else if (t.isStringLiteral(arg) || t.isNumericLiteral(arg)) {
|
||||
return arg.value;
|
||||
}
|
||||
else if (t.isIdentifier(arg)) {
|
||||
return arg.name;
|
||||
}
|
||||
else {
|
||||
return babel_generator_1.default(arg).code;
|
||||
}
|
||||
throw new Error(`bind 不支持传入该参数: ${arg}`);
|
||||
}
|
||||
exports.getArgumentName = getArgumentName;
|
||||
function isAllLiteral(...args) {
|
||||
return args.every(p => t.isLiteral(p));
|
||||
}
|
||||
exports.isAllLiteral = isAllLiteral;
|
||||
function reverseBoolean(expression) {
|
||||
return t.unaryExpression('!', expression);
|
||||
}
|
||||
exports.reverseBoolean = reverseBoolean;
|
||||
function isEmptyDeclarator(node) {
|
||||
if (t.isVariableDeclarator(node) &&
|
||||
(node.init === null ||
|
||||
t.isNullLiteral(node.init))) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
exports.isEmptyDeclarator = isEmptyDeclarator;
|
||||
function toLetters(num) {
|
||||
let mod = num % 26;
|
||||
let pow = num / 26 | 0;
|
||||
let out = mod ? String.fromCharCode(64 + mod) : (--pow, 'Z');
|
||||
const letter = pow ? toLetters(pow) + out : out;
|
||||
return letter.toLowerCase();
|
||||
}
|
||||
exports.toLetters = toLetters;
|
||||
function findIdentifierFromStatement(statement) {
|
||||
if (t.isVariableDeclaration(statement)) {
|
||||
const declarator = statement.declarations.find(s => t.isIdentifier(s.id));
|
||||
if (declarator && t.isIdentifier(declarator.id)) {
|
||||
return declarator.id.name;
|
||||
}
|
||||
}
|
||||
return '__return';
|
||||
}
|
||||
exports.findIdentifierFromStatement = findIdentifierFromStatement;
|
||||
//# sourceMappingURL=utils.js.map
|
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,93 @@
|
|||
{
|
||||
"name": "@tarojs/transformer-wx",
|
||||
"version": "1.2.13",
|
||||
"description": "Transfrom Nerv Component to Wechat mini program.",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/NervJS/taro.git"
|
||||
},
|
||||
"main": "index.js",
|
||||
"files": [
|
||||
"index.js",
|
||||
"lib",
|
||||
"cli.js",
|
||||
"dist"
|
||||
],
|
||||
"scripts": {
|
||||
"test:cov": "jest --coverage && npm run lint",
|
||||
"test": "jest",
|
||||
"dev": "tsc -w --pretty",
|
||||
"lint": "tslint",
|
||||
"build": "tsc"
|
||||
},
|
||||
"author": "O2Team",
|
||||
"license": "MIT",
|
||||
"jest": {
|
||||
"testEnvironment": "node",
|
||||
"transform": {
|
||||
"^.+\\.tsx?$": "ts-jest"
|
||||
},
|
||||
"testRegex": "(/__tests__/.*|(\\.|/)(test|spec))\\.(jsx?|tsx?)$",
|
||||
"moduleFileExtensions": [
|
||||
"ts",
|
||||
"tsx",
|
||||
"js",
|
||||
"jsx",
|
||||
"json",
|
||||
"node"
|
||||
],
|
||||
"testPathIgnorePatterns": [
|
||||
"node_modules",
|
||||
"utils"
|
||||
]
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/code-frame": "^7.0.0-beta.44",
|
||||
"babel-core": "^6.26.3",
|
||||
"babel-eslint": "^8.2.3",
|
||||
"babel-helper-evaluate-path": "^0.5.0",
|
||||
"babel-helper-mark-eval-scopes": "^0.4.3",
|
||||
"babel-helper-remove-or-void": "^0.4.3",
|
||||
"babel-plugin-danger-remove-unused-import": "^1.1.1",
|
||||
"babel-plugin-minify-dead-code": "^0.5.2",
|
||||
"babel-plugin-remove-dead-code": "^1.3.2",
|
||||
"babel-plugin-syntax-dynamic-import": "^6.18.0",
|
||||
"babel-plugin-transform-class-properties": "^6.24.1",
|
||||
"babel-plugin-transform-define": "^1.3.0",
|
||||
"babel-plugin-transform-es2015-template-literals": "^6.22.0",
|
||||
"babel-plugin-transform-flow-strip-types": "^6.22.0",
|
||||
"babel-traverse": "^6.26.0",
|
||||
"babel-types": "^6.26.0",
|
||||
"eslint": "^4.15.0",
|
||||
"eslint-plugin-taro": "1.2.13",
|
||||
"html": "^1.0.0",
|
||||
"lodash": "^4.17.5",
|
||||
"prettier": "^1.14.2",
|
||||
"typescript": "^3.2.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tarojs/taro": "1.2.13",
|
||||
"@types/babel-core": "^6.25.5",
|
||||
"@types/babel-generator": "^6.25.1",
|
||||
"@types/babel-template": "^6.25.0",
|
||||
"@types/babel-traverse": "6.25.3",
|
||||
"@types/babel-types": "^6.25.2",
|
||||
"@types/eslint": "^4.16.5",
|
||||
"@types/jest": "^22.2.3",
|
||||
"@types/lodash": "^4.14.105",
|
||||
"@types/node": "^9.6.2",
|
||||
"jest": "^23.0.1",
|
||||
"jest-cli": "^22.1.4",
|
||||
"ts-jest": "^22.4.6",
|
||||
"tslint": "^5.10.0",
|
||||
"tslint-config-prettier": "^1.10.0",
|
||||
"tslint-config-standard": "^7.0.0"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/NervJS/taro/issues"
|
||||
},
|
||||
"homepage": "https://github.com/NervJS/taro#readme"
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
/**app.wxss**/
|
||||
.container {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 200rpx 0;
|
||||
box-sizing: border-box;
|
||||
}
|
|
@ -0,0 +1,73 @@
|
|||
|
||||
import './app.css'
|
||||
import './pages/index/index'
|
||||
import { render, WeElement, define } from 'omi'
|
||||
|
||||
define('my-app', class extends WeElement {
|
||||
|
||||
config = {
|
||||
pages: [
|
||||
'pages/index/index',
|
||||
'pages/list/index',
|
||||
'pages/detail/index',
|
||||
'pages/logs/index'
|
||||
],
|
||||
window: {
|
||||
backgroundTextStyle: 'light',
|
||||
navigationBarBackgroundColor: '#fff',
|
||||
navigationBarTitleText: 'WeChat',
|
||||
navigationBarTextStyle: 'black'
|
||||
}
|
||||
}
|
||||
|
||||
globalData = {
|
||||
userInfo: null
|
||||
}
|
||||
|
||||
install() {
|
||||
// 展示本地存储能力
|
||||
var logs = wx.getStorageSync('logs') || []
|
||||
logs.unshift(Date.now())
|
||||
wx.setStorageSync('logs', logs)
|
||||
|
||||
// 登录
|
||||
wx.login({
|
||||
success: res => {
|
||||
// 发送 res.code 到后台换取 openId, sessionKey, unionId
|
||||
}
|
||||
})
|
||||
// 获取用户信息
|
||||
wx.getSetting({
|
||||
success: res => {
|
||||
if (res.authSetting['scope.userInfo']) {
|
||||
// 已经授权,可以直接调用 getUserInfo 获取头像昵称,不会弹框
|
||||
wx.getUserInfo({
|
||||
success: res => {
|
||||
// 可以将 res 发送给后台解码出 unionId
|
||||
this.globalData.userInfo = res.userInfo
|
||||
|
||||
// 由于 getUserInfo 是网络请求,可能会在 Page.onLoad 之后才返回
|
||||
// 所以此处加入 callback 以防止这种情况
|
||||
if (this.userInfoReadyCallback) {
|
||||
this.userInfoReadyCallback(res)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
onShow() { }
|
||||
|
||||
onHide() { }
|
||||
|
||||
render() {
|
||||
return (
|
||||
<page-index />
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
render(<my-app />, '#app')
|
|
@ -0,0 +1,6 @@
|
|||
button {
|
||||
width: 100px;
|
||||
height: 40px;
|
||||
line-height: 40px;
|
||||
display: inline-block;
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
|
||||
import { WeElement, define } from 'omi'
|
||||
import '../my-element'
|
||||
|
||||
import './index.css'
|
||||
|
||||
define('my-counter', class extends WeElement {
|
||||
data = {
|
||||
count: 1
|
||||
}
|
||||
|
||||
sub = () => {
|
||||
this.data.count--
|
||||
this.fire('change', this.data.count)
|
||||
this.update()
|
||||
}
|
||||
|
||||
add = () => {
|
||||
this.data.count++
|
||||
this.fire('change', this.data.count)
|
||||
this.update()
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<view>
|
||||
<button onClick={this.sub}>-</button>
|
||||
<text>{this.data.count}</text>
|
||||
<button onClick={this.add}>+</button>
|
||||
<my-element msg='omi'></my-element>
|
||||
</view>
|
||||
)
|
||||
}
|
||||
})
|
|
@ -0,0 +1,35 @@
|
|||
import { WeElement, define } from 'omi'
|
||||
|
||||
define('my-element', class extends WeElement {
|
||||
a = 1
|
||||
|
||||
data = {
|
||||
arr: ['item1', 'item2']
|
||||
}
|
||||
|
||||
clickHandler = () => {
|
||||
console.log('clicked' + this.a)
|
||||
|
||||
this.data.arr[0] = Math.random()
|
||||
this.data.arr[1] = Math.random()
|
||||
this.update()
|
||||
//上面代码等同于,注意 h5 不支持下面方式更新
|
||||
// this.update({
|
||||
// 'arr[0]': Math.random(),
|
||||
// "arr[1]": Math.random()
|
||||
// })
|
||||
}
|
||||
|
||||
render() {
|
||||
const { msg } = this.props
|
||||
const { arr } = this.data
|
||||
return (
|
||||
<view>
|
||||
<view>Hello, {msg}!</view>
|
||||
<button onClick={this.clickHandler}>点击我</button>
|
||||
<view>{arr[0]}</view>
|
||||
<view>{arr[1]}</view>
|
||||
</view>
|
||||
)
|
||||
}
|
||||
})
|
|
@ -0,0 +1,17 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
|
||||
<meta content="width=device-width,initial-scale=1,user-scalable=no" name="viewport">
|
||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||
<meta name="apple-touch-fullscreen" content="yes">
|
||||
<meta name="format-detection" content="telephone=no,address=no">
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="white">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" >
|
||||
<link rel="shortcut icon" href="https://tencent.github.io/omi/packages/omiu/examples/build/favicon.ico">
|
||||
<title>Omi</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,10 @@
|
|||
window.getApp = function () {
|
||||
return {
|
||||
globalData: {
|
||||
userInfo: {
|
||||
avatarUrl: 'https://github.com/Tencent/omi/raw/master/assets/omi-logo-140.png?sanitize=true',
|
||||
nickName: 'dntzhang(张磊)'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,2 @@
|
|||
import './wx'
|
||||
import './get-app'
|
|
@ -0,0 +1,182 @@
|
|||
import { render } from 'omi'
|
||||
import ajax from '@fdaciuk/ajax'
|
||||
|
||||
|
||||
function noop() { }
|
||||
|
||||
const wx = {}
|
||||
|
||||
wx.canIUse = function () {
|
||||
return false
|
||||
}
|
||||
|
||||
wx.getStorageSync = noop
|
||||
wx.setStorageSync = noop
|
||||
wx.login = noop
|
||||
wx.getSetting = noop
|
||||
|
||||
wx.getSystemInfoSync = function () {
|
||||
return {
|
||||
windowWidth: window.innerWidth,
|
||||
windowHeight: window.innerHeight,
|
||||
pixelRatio: window.devicePixelRadio
|
||||
}
|
||||
}
|
||||
|
||||
wx.getSystemInfo = function (options) {
|
||||
options.success({
|
||||
windowWidth: window.innerWidth,
|
||||
windowHeight: window.innerHeight,
|
||||
pixelRatio: window.devicePixelRadio
|
||||
})
|
||||
}
|
||||
|
||||
wx.getUserInfo = function () {
|
||||
console.warn('wx.getUserInfo method cannot be invoked in browser.')
|
||||
}
|
||||
|
||||
wx.navigateTo = function (option) {
|
||||
route.query = getUrlParams(option.url)
|
||||
route.to(option.url.replace('../','/pages/'), option)
|
||||
}
|
||||
|
||||
function getUrlParam(name, url) {
|
||||
if (!name) {
|
||||
return ''
|
||||
}
|
||||
url = url || location.search
|
||||
name = name.replace(/(?=[\\^$*+?.():|{}])/, '\\')
|
||||
var reg = new RegExp('(?:[?&]|^)' + name + '=([^?&#]*)', 'i')
|
||||
var match = url.match(reg)
|
||||
return !match ? '' : match[1]
|
||||
}
|
||||
|
||||
wx.setNavigationBarTitle = function (option) {
|
||||
document.title = option.title
|
||||
}
|
||||
|
||||
wx.navigateBack = function (option) {
|
||||
history.go(option.delta * -1)
|
||||
}
|
||||
|
||||
function getUrlParams(url) {
|
||||
url = url.replace(/#.*$/, '')
|
||||
var queryArray = url.split(/[?&]/).slice(1)
|
||||
var i, args = {}
|
||||
for (i = 0; i < queryArray.length; i++) {
|
||||
var match = queryArray[i].match(/([^=]+)=([^=]+)/)
|
||||
if (match !== null) {
|
||||
args[match[1]] = decodeURIComponent(match[2])
|
||||
}
|
||||
}
|
||||
return args
|
||||
}
|
||||
|
||||
wx.currentPage = null
|
||||
|
||||
export function routeUpdate(vnode, selector, byNative, root) {
|
||||
root.childNodes.forEach(child => {
|
||||
if (child.style.display !== 'none') {
|
||||
child.style.display = 'none'
|
||||
child.onHide && child.onHide()
|
||||
}
|
||||
})
|
||||
if (byNative) {
|
||||
const ele = document.querySelector(selector)
|
||||
if (ele) {
|
||||
ele.style.display = 'block'
|
||||
ele.onShow && ele.onShow()
|
||||
wx.currentPage = ele
|
||||
document.documentElement.scrollTop = ele._preScrollTop
|
||||
document.body.scrollTop = ele._preScrollTop
|
||||
//set twice
|
||||
setTimeout(function () {
|
||||
document.documentElement.scrollTop = ele._preScrollTop
|
||||
document.body.scrollTop = ele._preScrollTop
|
||||
}, 0)
|
||||
} else {
|
||||
const node = render(vnode, root)
|
||||
node.onShow && node.onShow()
|
||||
wx.currentPage = node
|
||||
document.documentElement.scrollTop = 0
|
||||
document.body.scrollTop = 0
|
||||
}
|
||||
} else {
|
||||
const ele = document.querySelector(selector)
|
||||
ele && ele.parentNode.removeChild(ele)
|
||||
const node = render(vnode, root)
|
||||
node.onShow && node.onShow()
|
||||
wx.currentPage = node
|
||||
document.documentElement.scrollTop = 0
|
||||
document.body.scrollTop = 0
|
||||
}
|
||||
}
|
||||
|
||||
wx.request = function (options) {
|
||||
const request = ajax({
|
||||
method: options.method || 'GET',
|
||||
url: options.url,
|
||||
data: options.data,
|
||||
headers: options.header
|
||||
})
|
||||
|
||||
request.then((data, xhr) => {
|
||||
options.success({
|
||||
data: data,
|
||||
statusCode: xhr.status
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
wx._bindReachBottom = function (callback, context) {
|
||||
window.addEventListener('scroll', () => {
|
||||
if (getScrollHeight() - getScrollTop() - getWindowHeight() < 30) {
|
||||
if(context === wx.currentPage){
|
||||
throttle(callback, context)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function getScrollHeight() {
|
||||
var scrollHeight = 0, bodyScrollHeight = 0, documentScrollHeight = 0;
|
||||
if (document.body) {
|
||||
bodyScrollHeight = document.body.scrollHeight;
|
||||
}
|
||||
if (document.documentElement) {
|
||||
documentScrollHeight = document.documentElement.scrollHeight;
|
||||
}
|
||||
scrollHeight = (bodyScrollHeight - documentScrollHeight > 0) ? bodyScrollHeight : documentScrollHeight;
|
||||
return scrollHeight;
|
||||
}
|
||||
|
||||
function getScrollTop() {
|
||||
var scrollTop = 0, bodyScrollTop = 0, documentScrollTop = 0;
|
||||
if (document.body) {
|
||||
bodyScrollTop = document.body.scrollTop;
|
||||
}
|
||||
if (document.documentElement) {
|
||||
documentScrollTop = document.documentElement.scrollTop;
|
||||
}
|
||||
scrollTop = (bodyScrollTop - documentScrollTop > 0) ? bodyScrollTop : documentScrollTop;
|
||||
return scrollTop;
|
||||
}
|
||||
|
||||
function getWindowHeight() {
|
||||
var windowHeight = 0;
|
||||
if (document.compatMode == "CSS1Compat") {
|
||||
windowHeight = document.documentElement.clientHeight;
|
||||
} else {
|
||||
windowHeight = document.body.clientHeight;
|
||||
}
|
||||
return windowHeight;
|
||||
}
|
||||
|
||||
function throttle(method, scope) {
|
||||
clearTimeout(method.tId);
|
||||
method.tId= setTimeout(function(){
|
||||
method.call(scope);
|
||||
}, 300);
|
||||
}
|
||||
|
||||
window.wx = wx
|
|
@ -0,0 +1,976 @@
|
|||
export = Omi;
|
||||
export as namespace Omi;
|
||||
|
||||
declare namespace Omi {
|
||||
type Callback = (...args: any[]) => void;
|
||||
type Key = string | number;
|
||||
type Ref<T> = (instance: T) => void;
|
||||
type ComponentChild = VNode<any> | object | string | number | boolean | null;
|
||||
type ComponentChildren = ComponentChild[] | ComponentChild;
|
||||
|
||||
interface Attributes {
|
||||
key?: string | number | any;
|
||||
}
|
||||
|
||||
interface ClassAttributes<T> extends Attributes {
|
||||
ref?: Ref<T>;
|
||||
}
|
||||
|
||||
interface OmiDOMAttributes {
|
||||
children?: ComponentChildren;
|
||||
dangerouslySetInnerHTML?: {
|
||||
__html: string;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Use this to manually set the attributes of a custom element
|
||||
*
|
||||
* declare global {
|
||||
* namespace JSX {
|
||||
* interface IntrinsicElements {
|
||||
* 'hello-element': CustomElementBaseAttributes & {
|
||||
* propFromParent: string;
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
*/
|
||||
interface CustomElementBaseAttributes extends ClassAttributes<any>, OmiDOMAttributes {}
|
||||
|
||||
/**
|
||||
* Define the contract for a virtual node in omi.
|
||||
*
|
||||
* A virtual node has a name, a map of attributes, an array
|
||||
* of child {VNode}s and a key. The key is used by omi for
|
||||
* internal purposes.
|
||||
*/
|
||||
interface VNode<P = any> {
|
||||
nodeName: string;
|
||||
attributes: P;
|
||||
children: Array<VNode<any> | string>;
|
||||
key?: Key | null;
|
||||
}
|
||||
|
||||
type RenderableProps<P, RefType = any> = Readonly<
|
||||
P & Attributes & { children?: ComponentChildren; ref?: Ref<RefType> }
|
||||
>;
|
||||
|
||||
interface WeElement<P, D> {
|
||||
install?(): void;
|
||||
installed?(): void;
|
||||
uninstall?(): void;
|
||||
beforeUpdate?(): void;
|
||||
afterUpdate?(): void;
|
||||
updated?(): void;
|
||||
beforeRender?(): void;
|
||||
receiveProps?(): void;
|
||||
}
|
||||
|
||||
interface ModelView<P, D> {
|
||||
install?(): void;
|
||||
installed?(): void;
|
||||
uninstall?(): void;
|
||||
beforeUpdate?(): void;
|
||||
afterUpdate?(): void;
|
||||
updated?(): void;
|
||||
beforeRender?(): void;
|
||||
receiveProps?(): void;
|
||||
}
|
||||
|
||||
interface Component<P, D> {
|
||||
install?(): void;
|
||||
installed?(): void;
|
||||
uninstall?(): void;
|
||||
beforeUpdate?(): void;
|
||||
afterUpdate?(): void;
|
||||
updated?(): void;
|
||||
beforeRender?(): void;
|
||||
receiveProps?(): void;
|
||||
}
|
||||
|
||||
abstract class WeElement<P = {}, D = {}> {
|
||||
constructor();
|
||||
|
||||
// Allow static members to reference class type parameters
|
||||
// https://github.com/Microsoft/TypeScript/issues/24018
|
||||
static props: object;
|
||||
static data: object;
|
||||
static observe: boolean;
|
||||
static mergeUpdate: boolean;
|
||||
static css: string;
|
||||
|
||||
props: RenderableProps<P>;
|
||||
data: D;
|
||||
host: HTMLElement;
|
||||
|
||||
update(): void;
|
||||
fire(name: string, data?: object): void;
|
||||
|
||||
// Abstract methods don't infer argument types
|
||||
// https://github.com/Microsoft/TypeScript/issues/14887
|
||||
abstract render(props: RenderableProps<P>, data: D): void;
|
||||
}
|
||||
|
||||
// The class type (not instance of class)
|
||||
// https://stackoverflow.com/q/42753968/2777142
|
||||
interface WeElementConstructor {
|
||||
new(): WeElement;
|
||||
}
|
||||
|
||||
abstract class ModelView<P = {}, D = {}> {
|
||||
constructor();
|
||||
|
||||
// Allow static members to reference class type parameters
|
||||
// https://github.com/Microsoft/TypeScript/issues/24018
|
||||
static props: object;
|
||||
static data: object;
|
||||
static observe: boolean;
|
||||
static mergeUpdate: boolean;
|
||||
|
||||
props: RenderableProps<P>;
|
||||
data: D;
|
||||
host: HTMLElement;
|
||||
|
||||
update(): void;
|
||||
fire(name: string, data?: object): void;
|
||||
|
||||
// Abstract methods don't infer argument types
|
||||
// https://github.com/Microsoft/TypeScript/issues/14887
|
||||
abstract render(props: RenderableProps<P>, data: D): void;
|
||||
}
|
||||
|
||||
abstract class Component<P = {}, D = {}> {
|
||||
constructor();
|
||||
|
||||
// Allow static members to reference class type parameters
|
||||
// https://github.com/Microsoft/TypeScript/issues/24018
|
||||
static props: object;
|
||||
static data: object;
|
||||
static observe: boolean;
|
||||
static mergeUpdate: boolean;
|
||||
static css: string;
|
||||
|
||||
props: RenderableProps<P>;
|
||||
data: D;
|
||||
host: HTMLElement;
|
||||
|
||||
update(): void;
|
||||
fire(name: string, data?: object): void;
|
||||
|
||||
// Abstract methods don't infer argument types
|
||||
// https://github.com/Microsoft/TypeScript/issues/14887
|
||||
abstract render(props: RenderableProps<P>, data: D): void;
|
||||
}
|
||||
|
||||
function h<P>(
|
||||
node: string,
|
||||
params: Attributes & P | null,
|
||||
...children: ComponentChildren[]
|
||||
): VNode<any>;
|
||||
function h(
|
||||
node: string,
|
||||
params: JSX.HTMLAttributes & JSX.SVGAttributes & Record<string, any> | null,
|
||||
...children: ComponentChildren[]
|
||||
): VNode<any>;
|
||||
|
||||
function render(vnode: ComponentChild, parent: string | Element | Document | ShadowRoot | DocumentFragment, store?: object, empty?: boolean, merge?: string | Element | Document | ShadowRoot | DocumentFragment): void;
|
||||
|
||||
function define(name: string, ctor: WeElementConstructor): void;
|
||||
function tag(name: string, pure?: boolean): (ctor: WeElementConstructor) => void;
|
||||
function tick(callback: Callback, scope?: any): void;
|
||||
function nextTick(callback: Callback, scope?: any): void;
|
||||
function observe(target: WeElementConstructor): void;
|
||||
function getHost(element: WeElement): WeElement;
|
||||
|
||||
var options: {
|
||||
vnode?: (vnode: VNode<any>) => void;
|
||||
event?: (event: Event) => Event;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
type Defaultize<Props, Defaults> =
|
||||
// Distribute over unions
|
||||
Props extends any
|
||||
? // Make any properties included in Default optional
|
||||
& Partial<Pick<Props, Extract<keyof Props, keyof Defaults>>>
|
||||
// Include the remaining properties from Props
|
||||
& Pick<Props, Exclude<keyof Props, keyof Defaults>>
|
||||
: never;
|
||||
|
||||
declare global {
|
||||
namespace JSX {
|
||||
interface Element extends Omi.VNode<any> {
|
||||
}
|
||||
|
||||
interface ElementClass extends Omi.WeElement<any, any> {
|
||||
}
|
||||
|
||||
interface ElementClass extends Omi.Component<any, any> {
|
||||
}
|
||||
|
||||
interface ElementAttributesProperty {
|
||||
props: any;
|
||||
}
|
||||
|
||||
interface ElementChildrenAttribute {
|
||||
children: any;
|
||||
}
|
||||
|
||||
type LibraryManagedAttributes<Component, Props> =
|
||||
Component extends { defaultProps: infer Defaults }
|
||||
? Defaultize<Props, Defaults>
|
||||
: Props;
|
||||
|
||||
interface SVGAttributes extends HTMLAttributes {
|
||||
accentHeight?: number | string;
|
||||
accumulate?: "none" | "sum";
|
||||
additive?: "replace" | "sum";
|
||||
alignmentBaseline?: "auto" | "baseline" | "before-edge" | "text-before-edge" | "middle" | "central" | "after-edge" | "text-after-edge" | "ideographic" | "alphabetic" | "hanging" | "mathematical" | "inherit";
|
||||
allowReorder?: "no" | "yes";
|
||||
alphabetic?: number | string;
|
||||
amplitude?: number | string;
|
||||
arabicForm?: "initial" | "medial" | "terminal" | "isolated";
|
||||
ascent?: number | string;
|
||||
attributeName?: string;
|
||||
attributeType?: string;
|
||||
autoReverse?: number | string;
|
||||
azimuth?: number | string;
|
||||
baseFrequency?: number | string;
|
||||
baselineShift?: number | string;
|
||||
baseProfile?: number | string;
|
||||
bbox?: number | string;
|
||||
begin?: number | string;
|
||||
bias?: number | string;
|
||||
by?: number | string;
|
||||
calcMode?: number | string;
|
||||
capHeight?: number | string;
|
||||
clip?: number | string;
|
||||
clipPath?: string;
|
||||
clipPathUnits?: number | string;
|
||||
clipRule?: number | string;
|
||||
colorInterpolation?: number | string;
|
||||
colorInterpolationFilters?: "auto" | "sRGB" | "linearRGB" | "inherit";
|
||||
colorProfile?: number | string;
|
||||
colorRendering?: number | string;
|
||||
contentScriptType?: number | string;
|
||||
contentStyleType?: number | string;
|
||||
cursor?: number | string;
|
||||
cx?: number | string;
|
||||
cy?: number | string;
|
||||
d?: string;
|
||||
decelerate?: number | string;
|
||||
descent?: number | string;
|
||||
diffuseConstant?: number | string;
|
||||
direction?: number | string;
|
||||
display?: number | string;
|
||||
divisor?: number | string;
|
||||
dominantBaseline?: number | string;
|
||||
dur?: number | string;
|
||||
dx?: number | string;
|
||||
dy?: number | string;
|
||||
edgeMode?: number | string;
|
||||
elevation?: number | string;
|
||||
enableBackground?: number | string;
|
||||
end?: number | string;
|
||||
exponent?: number | string;
|
||||
externalResourcesRequired?: number | string;
|
||||
fill?: string;
|
||||
fillOpacity?: number | string;
|
||||
fillRule?: "nonzero" | "evenodd" | "inherit";
|
||||
filter?: string;
|
||||
filterRes?: number | string;
|
||||
filterUnits?: number | string;
|
||||
floodColor?: number | string;
|
||||
floodOpacity?: number | string;
|
||||
focusable?: number | string;
|
||||
fontFamily?: string;
|
||||
fontSize?: number | string;
|
||||
fontSizeAdjust?: number | string;
|
||||
fontStretch?: number | string;
|
||||
fontStyle?: number | string;
|
||||
fontVariant?: number | string;
|
||||
fontWeight?: number | string;
|
||||
format?: number | string;
|
||||
from?: number | string;
|
||||
fx?: number | string;
|
||||
fy?: number | string;
|
||||
g1?: number | string;
|
||||
g2?: number | string;
|
||||
glyphName?: number | string;
|
||||
glyphOrientationHorizontal?: number | string;
|
||||
glyphOrientationVertical?: number | string;
|
||||
glyphRef?: number | string;
|
||||
gradientTransform?: string;
|
||||
gradientUnits?: string;
|
||||
hanging?: number | string;
|
||||
horizAdvX?: number | string;
|
||||
horizOriginX?: number | string;
|
||||
ideographic?: number | string;
|
||||
imageRendering?: number | string;
|
||||
in2?: number | string;
|
||||
in?: string;
|
||||
intercept?: number | string;
|
||||
k1?: number | string;
|
||||
k2?: number | string;
|
||||
k3?: number | string;
|
||||
k4?: number | string;
|
||||
k?: number | string;
|
||||
kernelMatrix?: number | string;
|
||||
kernelUnitLength?: number | string;
|
||||
kerning?: number | string;
|
||||
keyPoints?: number | string;
|
||||
keySplines?: number | string;
|
||||
keyTimes?: number | string;
|
||||
lengthAdjust?: number | string;
|
||||
letterSpacing?: number | string;
|
||||
lightingColor?: number | string;
|
||||
limitingConeAngle?: number | string;
|
||||
local?: number | string;
|
||||
markerEnd?: string;
|
||||
markerHeight?: number | string;
|
||||
markerMid?: string;
|
||||
markerStart?: string;
|
||||
markerUnits?: number | string;
|
||||
markerWidth?: number | string;
|
||||
mask?: string;
|
||||
maskContentUnits?: number | string;
|
||||
maskUnits?: number | string;
|
||||
mathematical?: number | string;
|
||||
mode?: number | string;
|
||||
numOctaves?: number | string;
|
||||
offset?: number | string;
|
||||
opacity?: number | string;
|
||||
operator?: number | string;
|
||||
order?: number | string;
|
||||
orient?: number | string;
|
||||
orientation?: number | string;
|
||||
origin?: number | string;
|
||||
overflow?: number | string;
|
||||
overlinePosition?: number | string;
|
||||
overlineThickness?: number | string;
|
||||
paintOrder?: number | string;
|
||||
panose1?: number | string;
|
||||
pathLength?: number | string;
|
||||
patternContentUnits?: string;
|
||||
patternTransform?: number | string;
|
||||
patternUnits?: string;
|
||||
pointerEvents?: number | string;
|
||||
points?: string;
|
||||
pointsAtX?: number | string;
|
||||
pointsAtY?: number | string;
|
||||
pointsAtZ?: number | string;
|
||||
preserveAlpha?: number | string;
|
||||
preserveAspectRatio?: string;
|
||||
primitiveUnits?: number | string;
|
||||
r?: number | string;
|
||||
radius?: number | string;
|
||||
refX?: number | string;
|
||||
refY?: number | string;
|
||||
renderingIntent?: number | string;
|
||||
repeatCount?: number | string;
|
||||
repeatDur?: number | string;
|
||||
requiredExtensions?: number | string;
|
||||
requiredFeatures?: number | string;
|
||||
restart?: number | string;
|
||||
result?: string;
|
||||
rotate?: number | string;
|
||||
rx?: number | string;
|
||||
ry?: number | string;
|
||||
scale?: number | string;
|
||||
seed?: number | string;
|
||||
shapeRendering?: number | string;
|
||||
slope?: number | string;
|
||||
spacing?: number | string;
|
||||
specularConstant?: number | string;
|
||||
specularExponent?: number | string;
|
||||
speed?: number | string;
|
||||
spreadMethod?: string;
|
||||
startOffset?: number | string;
|
||||
stdDeviation?: number | string;
|
||||
stemh?: number | string;
|
||||
stemv?: number | string;
|
||||
stitchTiles?: number | string;
|
||||
stopColor?: string;
|
||||
stopOpacity?: number | string;
|
||||
strikethroughPosition?: number | string;
|
||||
strikethroughThickness?: number | string;
|
||||
string?: number | string;
|
||||
stroke?: string;
|
||||
strokeDasharray?: string | number;
|
||||
strokeDashoffset?: string | number;
|
||||
strokeLinecap?: "butt" | "round" | "square" | "inherit";
|
||||
strokeLinejoin?: "miter" | "round" | "bevel" | "inherit";
|
||||
strokeMiterlimit?: string;
|
||||
strokeOpacity?: number | string;
|
||||
strokeWidth?: number | string;
|
||||
surfaceScale?: number | string;
|
||||
systemLanguage?: number | string;
|
||||
tableValues?: number | string;
|
||||
targetX?: number | string;
|
||||
targetY?: number | string;
|
||||
textAnchor?: string;
|
||||
textDecoration?: number | string;
|
||||
textLength?: number | string;
|
||||
textRendering?: number | string;
|
||||
to?: number | string;
|
||||
transform?: string;
|
||||
u1?: number | string;
|
||||
u2?: number | string;
|
||||
underlinePosition?: number | string;
|
||||
underlineThickness?: number | string;
|
||||
unicode?: number | string;
|
||||
unicodeBidi?: number | string;
|
||||
unicodeRange?: number | string;
|
||||
unitsPerEm?: number | string;
|
||||
vAlphabetic?: number | string;
|
||||
values?: string;
|
||||
vectorEffect?: number | string;
|
||||
version?: string;
|
||||
vertAdvY?: number | string;
|
||||
vertOriginX?: number | string;
|
||||
vertOriginY?: number | string;
|
||||
vHanging?: number | string;
|
||||
vIdeographic?: number | string;
|
||||
viewBox?: string;
|
||||
viewTarget?: number | string;
|
||||
visibility?: number | string;
|
||||
vMathematical?: number | string;
|
||||
widths?: number | string;
|
||||
wordSpacing?: number | string;
|
||||
writingMode?: number | string;
|
||||
x1?: number | string;
|
||||
x2?: number | string;
|
||||
x?: number | string;
|
||||
xChannelSelector?: string;
|
||||
xHeight?: number | string;
|
||||
xlinkActuate?: string;
|
||||
xlinkArcrole?: string;
|
||||
xlinkHref?: string;
|
||||
xlinkRole?: string;
|
||||
xlinkShow?: string;
|
||||
xlinkTitle?: string;
|
||||
xlinkType?: string;
|
||||
xmlBase?: string;
|
||||
xmlLang?: string;
|
||||
xmlns?: string;
|
||||
xmlnsXlink?: string;
|
||||
xmlSpace?: string;
|
||||
y1?: number | string;
|
||||
y2?: number | string;
|
||||
y?: number | string;
|
||||
yChannelSelector?: string;
|
||||
z?: number | string;
|
||||
zoomAndPan?: string;
|
||||
}
|
||||
|
||||
interface PathAttributes {
|
||||
d: string;
|
||||
}
|
||||
|
||||
interface EventHandler<E extends Event> {
|
||||
(event: E): void;
|
||||
}
|
||||
|
||||
type ClipboardEventHandler = EventHandler<ClipboardEvent>;
|
||||
type CompositionEventHandler = EventHandler<CompositionEvent>;
|
||||
type DragEventHandler = EventHandler<DragEvent>;
|
||||
type FocusEventHandler = EventHandler<FocusEvent>;
|
||||
type KeyboardEventHandler = EventHandler<KeyboardEvent>;
|
||||
type MouseEventHandler = EventHandler<MouseEvent>;
|
||||
type TouchEventHandler = EventHandler<TouchEvent>;
|
||||
type UIEventHandler = EventHandler<UIEvent>;
|
||||
type WheelEventHandler = EventHandler<WheelEvent>;
|
||||
type AnimationEventHandler = EventHandler<AnimationEvent>;
|
||||
type TransitionEventHandler = EventHandler<TransitionEvent>;
|
||||
type GenericEventHandler = EventHandler<Event>;
|
||||
type PointerEventHandler = EventHandler<PointerEvent>;
|
||||
|
||||
interface DOMAttributes extends Omi.OmiDOMAttributes {
|
||||
// Image Events
|
||||
onLoad?: GenericEventHandler;
|
||||
onError?: GenericEventHandler;
|
||||
onLoadCapture?: GenericEventHandler;
|
||||
|
||||
// Clipboard Events
|
||||
onCopy?: ClipboardEventHandler;
|
||||
onCopyCapture?: ClipboardEventHandler;
|
||||
onCut?: ClipboardEventHandler;
|
||||
onCutCapture?: ClipboardEventHandler;
|
||||
onPaste?: ClipboardEventHandler;
|
||||
onPasteCapture?: ClipboardEventHandler;
|
||||
|
||||
// Composition Events
|
||||
onCompositionEnd?: CompositionEventHandler;
|
||||
onCompositionEndCapture?: CompositionEventHandler;
|
||||
onCompositionStart?: CompositionEventHandler;
|
||||
onCompositionStartCapture?: CompositionEventHandler;
|
||||
onCompositionUpdate?: CompositionEventHandler;
|
||||
onCompositionUpdateCapture?: CompositionEventHandler;
|
||||
|
||||
// Focus Events
|
||||
onFocus?: FocusEventHandler;
|
||||
onFocusCapture?: FocusEventHandler;
|
||||
onBlur?: FocusEventHandler;
|
||||
onBlurCapture?: FocusEventHandler;
|
||||
|
||||
// Form Events
|
||||
onChange?: GenericEventHandler;
|
||||
onChangeCapture?: GenericEventHandler;
|
||||
onInput?: GenericEventHandler;
|
||||
onInputCapture?: GenericEventHandler;
|
||||
onSearch?: GenericEventHandler;
|
||||
onSearchCapture?: GenericEventHandler;
|
||||
onSubmit?: GenericEventHandler;
|
||||
onSubmitCapture?: GenericEventHandler;
|
||||
|
||||
// Keyboard Events
|
||||
onKeyDown?: KeyboardEventHandler;
|
||||
onKeyDownCapture?: KeyboardEventHandler;
|
||||
onKeyPress?: KeyboardEventHandler;
|
||||
onKeyPressCapture?: KeyboardEventHandler;
|
||||
onKeyUp?: KeyboardEventHandler;
|
||||
onKeyUpCapture?: KeyboardEventHandler;
|
||||
|
||||
// Media Events
|
||||
onAbort?: GenericEventHandler;
|
||||
onAbortCapture?: GenericEventHandler;
|
||||
onCanPlay?: GenericEventHandler;
|
||||
onCanPlayCapture?: GenericEventHandler;
|
||||
onCanPlayThrough?: GenericEventHandler;
|
||||
onCanPlayThroughCapture?: GenericEventHandler;
|
||||
onDurationChange?: GenericEventHandler;
|
||||
onDurationChangeCapture?: GenericEventHandler;
|
||||
onEmptied?: GenericEventHandler;
|
||||
onEmptiedCapture?: GenericEventHandler;
|
||||
onEncrypted?: GenericEventHandler;
|
||||
onEncryptedCapture?: GenericEventHandler;
|
||||
onEnded?: GenericEventHandler;
|
||||
onEndedCapture?: GenericEventHandler;
|
||||
onLoadedData?: GenericEventHandler;
|
||||
onLoadedDataCapture?: GenericEventHandler;
|
||||
onLoadedMetadata?: GenericEventHandler;
|
||||
onLoadedMetadataCapture?: GenericEventHandler;
|
||||
onLoadStart?: GenericEventHandler;
|
||||
onLoadStartCapture?: GenericEventHandler;
|
||||
onPause?: GenericEventHandler;
|
||||
onPauseCapture?: GenericEventHandler;
|
||||
onPlay?: GenericEventHandler;
|
||||
onPlayCapture?: GenericEventHandler;
|
||||
onPlaying?: GenericEventHandler;
|
||||
onPlayingCapture?: GenericEventHandler;
|
||||
onProgress?: GenericEventHandler;
|
||||
onProgressCapture?: GenericEventHandler;
|
||||
onRateChange?: GenericEventHandler;
|
||||
onRateChangeCapture?: GenericEventHandler;
|
||||
onSeeked?: GenericEventHandler;
|
||||
onSeekedCapture?: GenericEventHandler;
|
||||
onSeeking?: GenericEventHandler;
|
||||
onSeekingCapture?: GenericEventHandler;
|
||||
onStalled?: GenericEventHandler;
|
||||
onStalledCapture?: GenericEventHandler;
|
||||
onSuspend?: GenericEventHandler;
|
||||
onSuspendCapture?: GenericEventHandler;
|
||||
onTimeUpdate?: GenericEventHandler;
|
||||
onTimeUpdateCapture?: GenericEventHandler;
|
||||
onVolumeChange?: GenericEventHandler;
|
||||
onVolumeChangeCapture?: GenericEventHandler;
|
||||
onWaiting?: GenericEventHandler;
|
||||
onWaitingCapture?: GenericEventHandler;
|
||||
|
||||
// MouseEvents
|
||||
onClick?: MouseEventHandler;
|
||||
onClickCapture?: MouseEventHandler;
|
||||
onContextMenu?: MouseEventHandler;
|
||||
onContextMenuCapture?: MouseEventHandler;
|
||||
onDblClick?: MouseEventHandler;
|
||||
onDblClickCapture?: MouseEventHandler;
|
||||
onDrag?: DragEventHandler;
|
||||
onDragCapture?: DragEventHandler;
|
||||
onDragEnd?: DragEventHandler;
|
||||
onDragEndCapture?: DragEventHandler;
|
||||
onDragEnter?: DragEventHandler;
|
||||
onDragEnterCapture?: DragEventHandler;
|
||||
onDragExit?: DragEventHandler;
|
||||
onDragExitCapture?: DragEventHandler;
|
||||
onDragLeave?: DragEventHandler;
|
||||
onDragLeaveCapture?: DragEventHandler;
|
||||
onDragOver?: DragEventHandler;
|
||||
onDragOverCapture?: DragEventHandler;
|
||||
onDragStart?: DragEventHandler;
|
||||
onDragStartCapture?: DragEventHandler;
|
||||
onDrop?: DragEventHandler;
|
||||
onDropCapture?: DragEventHandler;
|
||||
onMouseDown?: MouseEventHandler;
|
||||
onMouseDownCapture?: MouseEventHandler;
|
||||
onMouseEnter?: MouseEventHandler;
|
||||
onMouseEnterCapture?: MouseEventHandler;
|
||||
onMouseLeave?: MouseEventHandler;
|
||||
onMouseLeaveCapture?: MouseEventHandler;
|
||||
onMouseMove?: MouseEventHandler;
|
||||
onMouseMoveCapture?: MouseEventHandler;
|
||||
onMouseOut?: MouseEventHandler;
|
||||
onMouseOutCapture?: MouseEventHandler;
|
||||
onMouseOver?: MouseEventHandler;
|
||||
onMouseOverCapture?: MouseEventHandler;
|
||||
onMouseUp?: MouseEventHandler;
|
||||
onMouseUpCapture?: MouseEventHandler;
|
||||
|
||||
// Selection Events
|
||||
onSelect?: GenericEventHandler;
|
||||
onSelectCapture?: GenericEventHandler;
|
||||
|
||||
// Touch Events
|
||||
onTouchCancel?: TouchEventHandler;
|
||||
onTouchCancelCapture?: TouchEventHandler;
|
||||
onTouchEnd?: TouchEventHandler;
|
||||
onTouchEndCapture?: TouchEventHandler;
|
||||
onTouchMove?: TouchEventHandler;
|
||||
onTouchMoveCapture?: TouchEventHandler;
|
||||
onTouchStart?: TouchEventHandler;
|
||||
onTouchStartCapture?: TouchEventHandler;
|
||||
|
||||
// Pointer Events
|
||||
onPointerOver?: PointerEventHandler;
|
||||
onPointerOverCapture?: PointerEventHandler;
|
||||
onPointerEnter?: PointerEventHandler;
|
||||
onPointerEnterCapture?: PointerEventHandler;
|
||||
onPointerDown?: PointerEventHandler;
|
||||
onPointerDownCapture?: PointerEventHandler;
|
||||
onPointerMove?: PointerEventHandler;
|
||||
onPointerMoveCapture?: PointerEventHandler;
|
||||
onPointerUp?: PointerEventHandler;
|
||||
onPointerUpCapture?: PointerEventHandler;
|
||||
onPointerCancel?: PointerEventHandler;
|
||||
onPointerCancelCapture?: PointerEventHandler;
|
||||
onPointerOut?: PointerEventHandler;
|
||||
onPointerOutCapture?: PointerEventHandler;
|
||||
onPointerLeave?: PointerEventHandler;
|
||||
onPointerLeaveCapture?: PointerEventHandler;
|
||||
onGotPointerCapture?: PointerEventHandler;
|
||||
onGotPointerCaptureCapture?: PointerEventHandler;
|
||||
onLostPointerCapture?: PointerEventHandler;
|
||||
onLostPointerCaptureCapture?: PointerEventHandler;
|
||||
|
||||
// UI Events
|
||||
onScroll?: UIEventHandler;
|
||||
onScrollCapture?: UIEventHandler;
|
||||
|
||||
// Wheel Events
|
||||
onWheel?: WheelEventHandler;
|
||||
onWheelCapture?: WheelEventHandler;
|
||||
|
||||
// Animation Events
|
||||
onAnimationStart?: AnimationEventHandler;
|
||||
onAnimationStartCapture?: AnimationEventHandler;
|
||||
onAnimationEnd?: AnimationEventHandler;
|
||||
onAnimationEndCapture?: AnimationEventHandler;
|
||||
onAnimationIteration?: AnimationEventHandler;
|
||||
onAnimationIterationCapture?: AnimationEventHandler;
|
||||
|
||||
// Transition Events
|
||||
onTransitionEnd?: TransitionEventHandler;
|
||||
onTransitionEndCapture?: TransitionEventHandler;
|
||||
}
|
||||
|
||||
interface HTMLAttributes extends Omi.ClassAttributes<any>, DOMAttributes {
|
||||
// Standard HTML Attributes
|
||||
accept?: string;
|
||||
acceptCharset?: string;
|
||||
accessKey?: string;
|
||||
action?: string;
|
||||
allowFullScreen?: boolean;
|
||||
allowTransparency?: boolean;
|
||||
alt?: string;
|
||||
async?: boolean;
|
||||
autocomplete?: string;
|
||||
autofocus?: boolean;
|
||||
autoPlay?: boolean;
|
||||
capture?: boolean;
|
||||
cellPadding?: number | string;
|
||||
cellSpacing?: number | string;
|
||||
charSet?: string;
|
||||
challenge?: string;
|
||||
checked?: boolean;
|
||||
class?: string;
|
||||
className?: string;
|
||||
cols?: number;
|
||||
colSpan?: number;
|
||||
content?: string;
|
||||
contentEditable?: boolean;
|
||||
contextMenu?: string;
|
||||
controls?: boolean;
|
||||
controlsList?: string;
|
||||
coords?: string;
|
||||
crossOrigin?: string;
|
||||
data?: string;
|
||||
dateTime?: string;
|
||||
default?: boolean;
|
||||
defer?: boolean;
|
||||
dir?: string;
|
||||
disabled?: boolean;
|
||||
download?: any;
|
||||
draggable?: boolean;
|
||||
encType?: string;
|
||||
form?: string;
|
||||
formAction?: string;
|
||||
formEncType?: string;
|
||||
formMethod?: string;
|
||||
formNoValidate?: boolean;
|
||||
formTarget?: string;
|
||||
frameBorder?: number | string;
|
||||
headers?: string;
|
||||
height?: number | string;
|
||||
hidden?: boolean;
|
||||
high?: number;
|
||||
href?: string;
|
||||
hrefLang?: string;
|
||||
for?: string;
|
||||
httpEquiv?: string;
|
||||
icon?: string;
|
||||
id?: string;
|
||||
inputMode?: string;
|
||||
integrity?: string;
|
||||
is?: string;
|
||||
keyParams?: string;
|
||||
keyType?: string;
|
||||
kind?: string;
|
||||
label?: string;
|
||||
lang?: string;
|
||||
list?: string;
|
||||
loop?: boolean;
|
||||
low?: number;
|
||||
manifest?: string;
|
||||
marginHeight?: number;
|
||||
marginWidth?: number;
|
||||
max?: number | string;
|
||||
maxLength?: number;
|
||||
media?: string;
|
||||
mediaGroup?: string;
|
||||
method?: string;
|
||||
min?: number | string;
|
||||
minLength?: number;
|
||||
multiple?: boolean;
|
||||
muted?: boolean;
|
||||
name?: string;
|
||||
noValidate?: boolean;
|
||||
open?: boolean;
|
||||
optimum?: number;
|
||||
pattern?: string;
|
||||
placeholder?: string;
|
||||
playsInline?: boolean;
|
||||
poster?: string;
|
||||
preload?: string;
|
||||
radioGroup?: string;
|
||||
readOnly?: boolean;
|
||||
rel?: string;
|
||||
required?: boolean;
|
||||
role?: string;
|
||||
rows?: number;
|
||||
rowSpan?: number;
|
||||
sandbox?: string;
|
||||
scope?: string;
|
||||
scoped?: boolean;
|
||||
scrolling?: string;
|
||||
seamless?: boolean;
|
||||
selected?: boolean;
|
||||
shape?: string;
|
||||
size?: number;
|
||||
sizes?: string;
|
||||
slot?: string;
|
||||
span?: number;
|
||||
spellcheck?: boolean;
|
||||
src?: string;
|
||||
srcset?: string;
|
||||
srcDoc?: string;
|
||||
srcLang?: string;
|
||||
srcSet?: string;
|
||||
start?: number;
|
||||
step?: number | string;
|
||||
style?: any;
|
||||
summary?: string;
|
||||
tabIndex?: number;
|
||||
target?: string;
|
||||
title?: string;
|
||||
type?: string;
|
||||
useMap?: string;
|
||||
value?: string | string[] | number;
|
||||
width?: number | string;
|
||||
wmode?: string;
|
||||
wrap?: string;
|
||||
|
||||
// RDFa Attributes
|
||||
about?: string;
|
||||
datatype?: string;
|
||||
inlist?: any;
|
||||
prefix?: string;
|
||||
property?: string;
|
||||
resource?: string;
|
||||
typeof?: string;
|
||||
vocab?: string;
|
||||
}
|
||||
|
||||
interface IntrinsicElements {
|
||||
// HTML
|
||||
a: HTMLAttributes;
|
||||
abbr: HTMLAttributes;
|
||||
address: HTMLAttributes;
|
||||
area: HTMLAttributes;
|
||||
article: HTMLAttributes;
|
||||
aside: HTMLAttributes;
|
||||
audio: HTMLAttributes;
|
||||
b: HTMLAttributes;
|
||||
base: HTMLAttributes;
|
||||
bdi: HTMLAttributes;
|
||||
bdo: HTMLAttributes;
|
||||
big: HTMLAttributes;
|
||||
blockquote: HTMLAttributes;
|
||||
body: HTMLAttributes;
|
||||
br: HTMLAttributes;
|
||||
button: HTMLAttributes;
|
||||
canvas: HTMLAttributes;
|
||||
caption: HTMLAttributes;
|
||||
cite: HTMLAttributes;
|
||||
code: HTMLAttributes;
|
||||
col: HTMLAttributes;
|
||||
colgroup: HTMLAttributes;
|
||||
data: HTMLAttributes;
|
||||
datalist: HTMLAttributes;
|
||||
dd: HTMLAttributes;
|
||||
del: HTMLAttributes;
|
||||
details: HTMLAttributes;
|
||||
dfn: HTMLAttributes;
|
||||
dialog: HTMLAttributes;
|
||||
div: HTMLAttributes;
|
||||
dl: HTMLAttributes;
|
||||
dt: HTMLAttributes;
|
||||
em: HTMLAttributes;
|
||||
embed: HTMLAttributes;
|
||||
fieldset: HTMLAttributes;
|
||||
figcaption: HTMLAttributes;
|
||||
figure: HTMLAttributes;
|
||||
footer: HTMLAttributes;
|
||||
form: HTMLAttributes;
|
||||
h1: HTMLAttributes;
|
||||
h2: HTMLAttributes;
|
||||
h3: HTMLAttributes;
|
||||
h4: HTMLAttributes;
|
||||
h5: HTMLAttributes;
|
||||
h6: HTMLAttributes;
|
||||
head: HTMLAttributes;
|
||||
header: HTMLAttributes;
|
||||
hr: HTMLAttributes;
|
||||
html: HTMLAttributes;
|
||||
i: HTMLAttributes;
|
||||
iframe: HTMLAttributes;
|
||||
img: HTMLAttributes;
|
||||
input: HTMLAttributes;
|
||||
ins: HTMLAttributes;
|
||||
kbd: HTMLAttributes;
|
||||
keygen: HTMLAttributes;
|
||||
label: HTMLAttributes;
|
||||
legend: HTMLAttributes;
|
||||
li: HTMLAttributes;
|
||||
link: HTMLAttributes;
|
||||
main: HTMLAttributes;
|
||||
map: HTMLAttributes;
|
||||
mark: HTMLAttributes;
|
||||
menu: HTMLAttributes;
|
||||
menuitem: HTMLAttributes;
|
||||
meta: HTMLAttributes;
|
||||
meter: HTMLAttributes;
|
||||
nav: HTMLAttributes;
|
||||
noscript: HTMLAttributes;
|
||||
object: HTMLAttributes;
|
||||
ol: HTMLAttributes;
|
||||
optgroup: HTMLAttributes;
|
||||
option: HTMLAttributes;
|
||||
output: HTMLAttributes;
|
||||
p: HTMLAttributes;
|
||||
param: HTMLAttributes;
|
||||
picture: HTMLAttributes;
|
||||
pre: HTMLAttributes;
|
||||
progress: HTMLAttributes;
|
||||
q: HTMLAttributes;
|
||||
rp: HTMLAttributes;
|
||||
rt: HTMLAttributes;
|
||||
ruby: HTMLAttributes;
|
||||
s: HTMLAttributes;
|
||||
samp: HTMLAttributes;
|
||||
script: HTMLAttributes;
|
||||
section: HTMLAttributes;
|
||||
select: HTMLAttributes;
|
||||
slot: HTMLAttributes;
|
||||
small: HTMLAttributes;
|
||||
source: HTMLAttributes;
|
||||
span: HTMLAttributes;
|
||||
strong: HTMLAttributes;
|
||||
style: HTMLAttributes;
|
||||
sub: HTMLAttributes;
|
||||
summary: HTMLAttributes;
|
||||
sup: HTMLAttributes;
|
||||
table: HTMLAttributes;
|
||||
tbody: HTMLAttributes;
|
||||
td: HTMLAttributes;
|
||||
textarea: HTMLAttributes;
|
||||
tfoot: HTMLAttributes;
|
||||
th: HTMLAttributes;
|
||||
thead: HTMLAttributes;
|
||||
time: HTMLAttributes;
|
||||
title: HTMLAttributes;
|
||||
tr: HTMLAttributes;
|
||||
track: HTMLAttributes;
|
||||
u: HTMLAttributes;
|
||||
ul: HTMLAttributes;
|
||||
"var": HTMLAttributes;
|
||||
video: HTMLAttributes;
|
||||
wbr: HTMLAttributes;
|
||||
|
||||
//SVG
|
||||
svg: SVGAttributes;
|
||||
animate: SVGAttributes;
|
||||
circle: SVGAttributes;
|
||||
clipPath: SVGAttributes;
|
||||
defs: SVGAttributes;
|
||||
ellipse: SVGAttributes;
|
||||
feBlend: SVGAttributes;
|
||||
feColorMatrix: SVGAttributes;
|
||||
feComponentTransfer: SVGAttributes;
|
||||
feComposite: SVGAttributes;
|
||||
feConvolveMatrix: SVGAttributes;
|
||||
feDiffuseLighting: SVGAttributes;
|
||||
feDisplacementMap: SVGAttributes;
|
||||
feFlood: SVGAttributes;
|
||||
feGaussianBlur: SVGAttributes;
|
||||
feImage: SVGAttributes;
|
||||
feMerge: SVGAttributes;
|
||||
feMergeNode: SVGAttributes;
|
||||
feMorphology: SVGAttributes;
|
||||
feOffset: SVGAttributes;
|
||||
feSpecularLighting: SVGAttributes;
|
||||
feTile: SVGAttributes;
|
||||
feTurbulence: SVGAttributes;
|
||||
filter: SVGAttributes;
|
||||
foreignObject: SVGAttributes;
|
||||
g: SVGAttributes;
|
||||
image: SVGAttributes;
|
||||
line: SVGAttributes;
|
||||
linearGradient: SVGAttributes;
|
||||
marker: SVGAttributes;
|
||||
mask: SVGAttributes;
|
||||
path: SVGAttributes;
|
||||
pattern: SVGAttributes;
|
||||
polygon: SVGAttributes;
|
||||
polyline: SVGAttributes;
|
||||
radialGradient: SVGAttributes;
|
||||
rect: SVGAttributes;
|
||||
stop: SVGAttributes;
|
||||
symbol: SVGAttributes;
|
||||
text: SVGAttributes;
|
||||
tspan: SVGAttributes;
|
||||
use: SVGAttributes;
|
||||
[tagName: string]: any;
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,13 @@
|
|||
/* @flow */
|
||||
|
||||
import { createElement, cloneElement, Component, type Node } from 'react';
|
||||
|
||||
declare var h: createElement;
|
||||
|
||||
declare function render(vnode: Node, parent: Element, toReplace?: Element): Element;
|
||||
|
||||
export { h, createElement, cloneElement, Component, render };
|
||||
export default { h, createElement, cloneElement, Component, render };
|
||||
|
||||
declare export function rerender(): void;
|
||||
declare export var options: Object;
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue