├── .gitignore
├── .prettierignore
├── .prettierrc
├── LICENSE
├── README.md
├── example
├── js-taro-react
│ ├── .editorconfig
│ ├── .eslintrc
│ ├── .npmrc
│ ├── babel.config.js
│ ├── config
│ │ ├── dev.js
│ │ ├── index.js
│ │ └── prod.js
│ ├── package-lock.json
│ ├── package.json
│ ├── project.config.json
│ ├── src
│ │ ├── app.config.js
│ │ ├── app.css
│ │ ├── app.js
│ │ ├── components
│ │ │ └── BaseComponent.jsx
│ │ ├── index.html
│ │ ├── package-test
│ │ │ └── pages
│ │ │ │ └── test
│ │ │ │ ├── index.config.js
│ │ │ │ └── index.jsx
│ │ └── pages
│ │ │ ├── arrowa
│ │ │ └── index.jsx
│ │ │ ├── arrowb
│ │ │ └── index.jsx
│ │ │ ├── arrowc
│ │ │ └── index.jsx
│ │ │ ├── classa
│ │ │ └── index.jsx
│ │ │ ├── classb
│ │ │ └── index.jsx
│ │ │ ├── classc
│ │ │ └── index.jsx
│ │ │ ├── connectarrow
│ │ │ └── index.jsx
│ │ │ ├── connectclass
│ │ │ └── index.jsx
│ │ │ ├── connectfunction
│ │ │ └── index.jsx
│ │ │ ├── functiona
│ │ │ └── index.jsx
│ │ │ ├── functionb
│ │ │ └── index.jsx
│ │ │ ├── functionc
│ │ │ └── index.jsx
│ │ │ └── index
│ │ │ ├── index.config.js
│ │ │ ├── index.css
│ │ │ └── index.jsx
│ └── yarn.lock
└── ts-taro-react
│ ├── .editorconfig
│ ├── .eslintrc
│ ├── .npmrc
│ ├── babel.config.js
│ ├── config
│ ├── dev.js
│ ├── index.js
│ └── prod.js
│ ├── global.d.ts
│ ├── log.js
│ ├── package-lock.json
│ ├── package.json
│ ├── project.config.json
│ ├── src
│ ├── app.config.ts
│ ├── app.scss
│ ├── app.ts
│ ├── components
│ │ └── BaseComponent.tsx
│ ├── index.html
│ ├── package-test
│ │ └── pages
│ │ │ └── test
│ │ │ ├── index.config.ts
│ │ │ └── index.tsx
│ └── pages
│ │ └── index
│ │ ├── index.config.ts
│ │ ├── index.scss
│ │ └── index.tsx
│ ├── tsconfig.json
│ └── yarn.lock
├── package.json
├── src
└── index.ts
└── tsconfig.json
/.gitignore:
--------------------------------------------------------------------------------
1 | *.log
2 | .DS_Store
3 | node_modules
4 | dist
5 | yarn.lock
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | dist
3 | test
4 |
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "semi": false,
3 | "tabWidth": 2,
4 | "singleQuote": true,
5 | "trailingComma": "all",
6 | "printWidth": 100
7 | }
8 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 Thomas Trainset
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Taro React 小程序注入全局组件
2 |
3 | ## 缘由
4 |
5 | 众所周知,小程序中不能定义全局组件,如果我们想自定义一个 modal 弹窗,来替代 **wx.showModal** 的话,则需要在每个页面手动的引入组件。随着项目越来越大,手动引入组件无疑是繁琐和低效的,因而开发了这个 webpack-loader 代替手动操作。
6 |
7 | ## 作用
8 |
9 | 为每个页面注入全局组件。支持主包和分包,支持类写法和函数写法。
10 |
11 | ## 环境
12 |
13 | `taro react`
14 |
15 | ## 安装
16 |
17 | `npm install taro-inject-component-loader -D` 。
18 |
19 | ## 配置
20 |
21 | ### Webpack 配置
22 |
23 | ```ts
24 | webpackChain(chain) {
25 | chain.merge({
26 | module: {
27 | rule: {
28 | injectBaseComponentLoader: {
29 | test: /\.tsx$/,
30 | use: [
31 | {
32 | loader: 'taro-inject-component-loader',
33 | logError: true,
34 | options: {
35 | importPath: '@components/BaseComponent',
36 | isPage(filePath) {
37 | // 兼容 windows
38 | const formatFilePath = filePath.replace(/\\/g, '/')
39 | return /(package-.+\/)?pages\/[A-Za-z0-9-]+\/index\.[tj]sx\$/.test(filePath)
40 | }
41 | },
42 | },
43 | ],
44 | },
45 | },
46 | },
47 | });
48 | },
49 | ```
50 |
51 | ### 配置项
52 |
53 | | 字段 | 必填 | 默认 | 含义 |
54 | | ---------- | ---- | --------------------------------------------------------------------------- | ---------------------- |
55 | | importPath | 是 | 无 | 导入路径 |
56 | | logError | 否 | true | 控制台打印错误 |
57 | | isPage | 否 | (path) => /(package-.+\/)?pages\/[A-Za-z0-9-]+\/index\.[tj]sx\$/.test(path) | 判断当前文件是不是页面 |
58 |
59 | isPage 不传的情况下,默认会将 `src/pages/页面名称/index.[tj]sx` 和 `src/package-模块名称/pages/页面名称/index.[tj]sx` 这两种情形下的文件识别为页面。
60 |
61 | ## 效果
62 |
63 | ### 源代码
64 |
65 | 页面组件
66 |
67 | ```tsx
68 |
69 | import { View } from '@taro/components'
70 |
71 | export default function Index() {
72 | return 哈哈哈哈哈
73 | }
74 | ```
75 |
76 | 要注入的组件
77 |
78 | ```tsx
79 |
80 | import { View } from '@taro/components'
81 |
82 | export default function () {
83 | return WebpackInject
84 | }
85 | ```
86 |
87 | ### 注入后的代码
88 |
89 | 会自动注入为页面根节点的最后一个子元素
90 |
91 | ```tsx
92 |
93 | import { View } from '@taro/components'
94 | import WebpackInject from '@components/BaseComponent'
95 |
96 | export default function Index() {
97 | return (
98 |
99 | 哈哈哈哈哈
100 |
101 |
102 | )
103 | }
104 | ```
105 |
106 | ## 语法支持
107 |
108 | 下面提到的写法中,都支持注入组件。对于识别为页面,但由于语法不支持,导致注入失败的页面,loader 会在控制台抛出警告,请注意查看。
109 |
110 | ```tsx
111 | // 导出匿名函数
112 | export default function() {
113 | return
114 | }
115 |
116 | // 导出具名函数
117 | export default function A() {
118 | return
119 | }
120 |
121 | // 导出匿名箭头函数
122 | export default () => {
123 | return
124 | }
125 |
126 | export default () =>
127 |
128 | // 导出匿名类
129 | export default class {
130 | render() {
131 | return
132 | }
133 | }
134 |
135 | // 导出具名类
136 | export default class A {
137 | render() {
138 | return
139 | }
140 | }
141 | ```
142 |
143 | 此外,还可以使用表达式导出
144 |
145 | ```tsx
146 | // 导出普通函数
147 | function A() {
148 | return
149 | }
150 |
151 | const A = function() {
152 | return
153 | }
154 |
155 | // 导出箭头函数
156 | const A = () => {
157 | return
158 | }
159 | const A = () =>
160 |
161 | // 导出类
162 | class A {
163 | render() {
164 | return
165 | }
166 | }
167 |
168 | const A = class {
169 | render() {
170 | return
171 | }
172 | }
173 |
174 | const A = class extends Component {
175 | render() {
176 | return
177 | }
178 | }
179 |
180 | export default A
181 | ```
182 |
183 | ## 注意事项
184 |
185 | ### 要注入的组件需要默认导出
186 |
187 | ```tsx
188 | import { View } from '@taro/components'
189 |
190 | // 默认导出
191 | export default () =>
192 | ```
193 |
194 | ### 跳过代码注入
195 |
196 | 只要检测到页面里有 importPath 的路径,无论代码中有没有用到该组件,都不会自动注入。
197 |
198 | ```tsx
199 | import { View } from '@taro/components'
200 | // 因为检测到了手动从 importPath 路径导入组件, 所以不会注入组件
201 | import WebpackInject from 'importPath'
202 |
203 | export default () =>
204 | ```
205 |
206 | ### 高阶组件代码注入
207 |
208 | loader 不支持高阶组件的代码注入
209 |
210 | ```tsx
211 | const connect = () => {
212 | return CComponent => CComponent
213 | }
214 |
215 | const CustomComponent = () => {
216 | return export default connect arrow function Component
217 | }
218 |
219 | export default memo(connect()(CustomComponent))
220 | ```
221 |
222 | 建议手动引入要注入的组件,或者更改代码写法:
223 |
224 | ```tsx
225 | const connect = () => {
226 | return CComponent => CComponent
227 | }
228 |
229 | const CustomComponent = () => {
230 | return export default connect arrow function Component
231 | }
232 |
233 | const HigherComponent = memo(connect()(CustomComponent))
234 |
235 | export default props => {
236 | return (
237 |
238 |
239 |
240 | )
241 | }
242 | ```
243 |
244 | ### 自闭合标签
245 |
246 | 自闭合标签组件不支持组件注入。
247 |
248 | ```tsx
249 | export default () =>
250 | ```
251 |
252 | 建议手动引入组件,或者将代码改为:
253 |
254 | ```tsx
255 | export default () => (
256 |
257 |
258 |
259 | )
260 | ```
261 |
262 | ### 空标签
263 |
264 | 空标签不支持组件注入。在编译的过程中,空标签会被剥去,导致找不到注入口。
265 |
266 | ```tsx
267 | export default () => (
268 | return (
269 | <>
270 |
271 |
272 |
273 |
274 | >
275 | )
276 | )
277 | ```
278 |
279 | 建议手动引入组件,或者将代码改为:
280 |
281 | ```tsx
282 | export default () => (
283 | return (
284 |
285 |
286 |
287 |
288 |
289 |
290 | )
291 | )
292 | ```
293 |
294 | ## 代码示例
295 |
296 | [ts 版本](example/ts-taro-react/config/index.js)
297 |
298 | [js 版本](example/js-taro-react/config/index.js)
299 |
--------------------------------------------------------------------------------
/example/js-taro-react/.editorconfig:
--------------------------------------------------------------------------------
1 | # http://editorconfig.org
2 | root = true
3 |
4 | [*]
5 | indent_style = space
6 | indent_size = 2
7 | charset = utf-8
8 | trim_trailing_whitespace = true
9 | insert_final_newline = true
10 |
11 | [*.md]
12 | trim_trailing_whitespace = false
13 |
--------------------------------------------------------------------------------
/example/js-taro-react/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "extends": ["taro/react"]
3 | }
4 |
--------------------------------------------------------------------------------
/example/js-taro-react/.npmrc:
--------------------------------------------------------------------------------
1 | registry=https://registry.npm.taobao.org
2 | disturl=https://npm.taobao.org/dist
3 | sass_binary_site=https://npm.taobao.org/mirrors/node-sass/
4 | phantomjs_cdnurl=https://npm.taobao.org/mirrors/phantomjs/
5 | electron_mirror=https://npm.taobao.org/mirrors/electron/
6 | chromedriver_cdnurl=https://npm.taobao.org/mirrors/chromedriver
7 | operadriver_cdnurl=https://npm.taobao.org/mirrors/operadriver
8 | selenium_cdnurl=https://npm.taobao.org/mirrors/selenium
9 | node_inspector_cdnurl=https://npm.taobao.org/mirrors/node-inspector
10 | fsevents_binary_host_mirror=http://npm.taobao.org/mirrors/fsevents/
11 |
--------------------------------------------------------------------------------
/example/js-taro-react/babel.config.js:
--------------------------------------------------------------------------------
1 | // babel-preset-taro 更多选项和默认值:
2 | // https://github.com/NervJS/taro/blob/next/packages/babel-preset-taro/README.md
3 | module.exports = {
4 | presets: [
5 | ['taro', {
6 | framework: 'react',
7 | ts: false
8 | }]
9 | ]
10 | }
11 |
--------------------------------------------------------------------------------
/example/js-taro-react/config/dev.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | env: {
3 | NODE_ENV: '"development"'
4 | },
5 | defineConstants: {
6 | },
7 | mini: {},
8 | h5: {}
9 | }
10 |
--------------------------------------------------------------------------------
/example/js-taro-react/config/index.js:
--------------------------------------------------------------------------------
1 | const path = require('path')
2 |
3 | const config = {
4 | projectName: 'js-taro-react',
5 | date: '2021-2-16',
6 | designWidth: 750,
7 | deviceRatio: {
8 | 640: 2.34 / 2,
9 | 750: 1,
10 | 828: 1.81 / 2,
11 | },
12 | sourceRoot: 'src',
13 | outputRoot: 'dist',
14 | plugins: [],
15 | defineConstants: {},
16 | copy: {
17 | patterns: [],
18 | options: {},
19 | },
20 | alias: {
21 | '@components': path.resolve(__dirname, '..', 'src/components'),
22 | },
23 | framework: 'react',
24 | mini: {
25 | postcss: {
26 | pxtransform: {
27 | enable: true,
28 | config: {},
29 | },
30 | url: {
31 | enable: true,
32 | config: {
33 | limit: 1024, // 设定转换尺寸上限
34 | },
35 | },
36 | cssModules: {
37 | enable: false, // 默认为 false,如需使用 css modules 功能,则设为 true
38 | config: {
39 | namingPattern: 'module', // 转换模式,取值为 global/module
40 | generateScopedName: '[name]__[local]___[hash:base64:5]',
41 | },
42 | },
43 | },
44 | webpackChain(chain) {
45 | chain.merge({
46 | module: {
47 | rule: {
48 | injectBaseComponentLoader: {
49 | test: /\.jsx$/,
50 | use: [
51 | {
52 | loader: path.resolve(__dirname, '../../../dist/index.js'),
53 | options: {
54 | importPath: '@components/BaseComponent',
55 | },
56 | },
57 | ],
58 | },
59 | },
60 | },
61 | })
62 | },
63 | },
64 | h5: {
65 | publicPath: '/',
66 | staticDirectory: 'static',
67 | postcss: {
68 | autoprefixer: {
69 | enable: true,
70 | config: {},
71 | },
72 | cssModules: {
73 | enable: false, // 默认为 false,如需使用 css modules 功能,则设为 true
74 | config: {
75 | namingPattern: 'module', // 转换模式,取值为 global/module
76 | generateScopedName: '[name]__[local]___[hash:base64:5]',
77 | },
78 | },
79 | },
80 | webpackChain(chain) {
81 | chain.merge({
82 | module: {
83 | rule: {
84 | injectBaseComponentLoader: {
85 | test: /\.jsx$/,
86 | use: [
87 | {
88 | loader: path.resolve(__dirname, '../../../dist/index.js'),
89 | options: {
90 | importSpecifier: '@components/BaseComponent',
91 | componentName: 'BaseComponent',
92 | isPage(filePath) {
93 | return /(package-.+\/)?pages\/.+\/index\.jsx$/.test(filePath)
94 | },
95 | },
96 | },
97 | ],
98 | },
99 | },
100 | },
101 | })
102 | },
103 | },
104 | }
105 |
106 | module.exports = function(merge) {
107 | if (process.env.NODE_ENV === 'development') {
108 | return merge({}, config, require('./dev'))
109 | }
110 | return merge({}, config, require('./prod'))
111 | }
112 |
--------------------------------------------------------------------------------
/example/js-taro-react/config/prod.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | env: {
3 | NODE_ENV: '"production"'
4 | },
5 | defineConstants: {
6 | },
7 | mini: {},
8 | h5: {
9 | /**
10 | * 如果h5端编译后体积过大,可以使用webpack-bundle-analyzer插件对打包体积进行分析。
11 | * 参考代码如下:
12 | * webpackChain (chain) {
13 | * chain.plugin('analyzer')
14 | * .use(require('webpack-bundle-analyzer').BundleAnalyzerPlugin, [])
15 | * }
16 | */
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/example/js-taro-react/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "js-taro-react",
3 | "version": "1.0.0",
4 | "private": true,
5 | "description": "js-taro-react",
6 | "templateInfo": {
7 | "name": "default",
8 | "typescript": false,
9 | "css": "none"
10 | },
11 | "scripts": {
12 | "build:weapp": "taro build --type weapp",
13 | "build:swan": "taro build --type swan",
14 | "build:alipay": "taro build --type alipay",
15 | "build:tt": "taro build --type tt",
16 | "build:h5": "taro build --type h5",
17 | "build:rn": "taro build --type rn",
18 | "build:qq": "taro build --type qq",
19 | "build:jd": "taro build --type jd",
20 | "build:quickapp": "taro build --type quickapp",
21 | "dev:weapp": "npm run build:weapp -- --watch",
22 | "dev:swan": "npm run build:swan -- --watch",
23 | "dev:alipay": "npm run build:alipay -- --watch",
24 | "dev:tt": "npm run build:tt -- --watch",
25 | "dev:h5": "npm run build:h5 -- --watch",
26 | "dev:rn": "npm run build:rn -- --watch",
27 | "dev:qq": "npm run build:qq -- --watch",
28 | "dev:jd": "npm run build:jd -- --watch",
29 | "dev:quickapp": "npm run build:quickapp -- --watch"
30 | },
31 | "browserslist": [
32 | "last 3 versions",
33 | "Android >= 4.1",
34 | "ios >= 8"
35 | ],
36 | "author": "",
37 | "dependencies": {
38 | "@babel/runtime": "^7.7.7",
39 | "@styli/taro": "0.22.0",
40 | "@tarojs/components": "3.1.0-beta.4",
41 | "@tarojs/react": "3.1.0-beta.4",
42 | "@tarojs/runtime": "3.1.0-beta.4",
43 | "@tarojs/taro": "3.1.0-beta.4",
44 | "react": "^16.10.0",
45 | "react-dom": "^16.10.0"
46 | },
47 | "devDependencies": {
48 | "@babel/core": "^7.8.0",
49 | "@tarojs/mini-runner": "3.1.0-beta.4",
50 | "@tarojs/webpack-runner": "3.1.0-beta.4",
51 | "@types/react": "^16.0.0",
52 | "@types/webpack-env": "^1.13.6",
53 | "babel-preset-taro": "3.1.0-beta.4",
54 | "eslint": "^6.8.0",
55 | "eslint-config-taro": "3.1.0-beta.4",
56 | "eslint-plugin-import": "^2.12.0",
57 | "eslint-plugin-react": "^7.8.2",
58 | "eslint-plugin-react-hooks": "^1.6.1",
59 | "stylelint": "9.3.0"
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/example/js-taro-react/project.config.json:
--------------------------------------------------------------------------------
1 | {
2 | "miniprogramRoot": "dist/",
3 | "projectname": "js-taro-react",
4 | "description": "js-taro-react",
5 | "appid": "wx86d4ff7b691abb62",
6 | "setting": {
7 | "urlCheck": true,
8 | "es6": false,
9 | "enhance": false,
10 | "postcss": false,
11 | "preloadBackgroundData": false,
12 | "minified": false,
13 | "newFeature": false,
14 | "coverView": true,
15 | "nodeModules": false,
16 | "autoAudits": false,
17 | "showShadowRootInWxmlPanel": true,
18 | "scopeDataCheck": false,
19 | "uglifyFileName": false,
20 | "checkInvalidKey": true,
21 | "checkSiteMap": true,
22 | "uploadWithSourceMap": true,
23 | "compileHotReLoad": false,
24 | "useMultiFrameRuntime": true,
25 | "useApiHook": true,
26 | "useApiHostProcess": false,
27 | "babelSetting": {
28 | "ignore": [],
29 | "disablePlugins": [],
30 | "outputPath": ""
31 | },
32 | "enableEngineNative": false,
33 | "bundle": false,
34 | "useIsolateContext": true,
35 | "useCompilerModule": true,
36 | "userConfirmedUseCompilerModuleSwitch": false,
37 | "userConfirmedBundleSwitch": false,
38 | "packNpmManually": false,
39 | "packNpmRelationList": [],
40 | "minifyWXSS": true
41 | },
42 | "compileType": "miniprogram",
43 | "condition": {}
44 | }
--------------------------------------------------------------------------------
/example/js-taro-react/src/app.config.js:
--------------------------------------------------------------------------------
1 | export default {
2 | pages: [
3 | 'pages/index/index',
4 | 'pages/classa/index',
5 | 'pages/classb/index',
6 | 'pages/classc/index',
7 | 'pages/functiona/index',
8 | 'pages/functionb/index',
9 | 'pages/functionc/index',
10 | 'pages/arrowa/index',
11 | 'pages/arrowb/index',
12 | 'pages/connectclass/index',
13 | 'pages/connectfunction/index',
14 | 'pages/connectarrow/index',
15 | ],
16 | subPackages: [
17 | {
18 | root: 'package-test',
19 | name: 'test',
20 | pages: ['pages/test/index'],
21 | },
22 | ],
23 | window: {
24 | backgroundTextStyle: 'light',
25 | navigationBarBackgroundColor: '#fff',
26 | navigationBarTitleText: 'WeChat',
27 | navigationBarTextStyle: 'black',
28 | },
29 | }
30 |
--------------------------------------------------------------------------------
/example/js-taro-react/src/app.css:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xdoer/taro-inject-component-loader/30354b13da365152c67b6bf383eb2b32e23dbd27/example/js-taro-react/src/app.css
--------------------------------------------------------------------------------
/example/js-taro-react/src/app.js:
--------------------------------------------------------------------------------
1 | import { Component } from 'react'
2 | import './app.css'
3 |
4 | class App extends Component {
5 |
6 | componentDidMount () {}
7 |
8 | componentDidShow () {}
9 |
10 | componentDidHide () {}
11 |
12 | componentDidCatchError () {}
13 |
14 | // this.props.children 是将要会渲染的页面
15 | render () {
16 | return this.props.children
17 | }
18 | }
19 |
20 | export default App
21 |
--------------------------------------------------------------------------------
/example/js-taro-react/src/components/BaseComponent.jsx:
--------------------------------------------------------------------------------
1 | import { View } from '@styli/taro'
2 | import React, { FC, memo } from 'react'
3 |
4 | export default memo(({}) => {
5 | console.log(1111)
6 | return (
7 |
8 |
9 | BaseComponent
10 |
11 |
12 | webpack inject
13 |
14 |
15 | )
16 | })
17 |
--------------------------------------------------------------------------------
/example/js-taro-react/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/example/js-taro-react/src/package-test/pages/test/index.config.js:
--------------------------------------------------------------------------------
1 | export default {
2 | navigationBarTitleText: '分包子页面-函数式组件',
3 | }
4 |
--------------------------------------------------------------------------------
/example/js-taro-react/src/package-test/pages/test/index.jsx:
--------------------------------------------------------------------------------
1 | import React, { memo } from 'react'
2 | import { View } from '@styli/taro'
3 |
4 | export default memo(function() {
5 | return (
6 |
7 |
8 | )
9 | })
10 |
--------------------------------------------------------------------------------
/example/js-taro-react/src/pages/arrowa/index.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import { View } from '@styli/taro'
3 |
4 | class CustomComponent3 extends Component {
5 | render() {
6 | return Component C
7 | }
8 | }
9 |
10 | const CustomComponent2 = () => Component B
11 |
12 | const CustomComponent = () => {
13 | return (
14 |
15 | export default arrow function Component A
16 |
17 |
18 |
19 | )
20 | }
21 |
22 | export default CustomComponent
23 |
--------------------------------------------------------------------------------
/example/js-taro-react/src/pages/arrowb/index.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import { View } from '@styli/taro'
3 |
4 | class CustomComponent3 extends Component {
5 | render() {
6 | return Component C
7 | }
8 | }
9 |
10 | const CustomComponent2 = () => Component B
11 |
12 | export default () => {
13 | return (
14 |
15 | export default arrow function Component A
16 |
17 |
18 |
19 | )
20 | }
21 |
--------------------------------------------------------------------------------
/example/js-taro-react/src/pages/arrowc/index.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { View } from '@styli/taro'
3 |
4 | const CustomComponent = () => {
5 | return export default arrow function Component A
6 | }
7 |
8 | export default CustomComponent
9 |
--------------------------------------------------------------------------------
/example/js-taro-react/src/pages/classa/index.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import { View } from '@styli/taro'
3 |
4 | class CustomComponent extends Component {
5 | render() {
6 | return export default class Component A
7 | }
8 | }
9 |
10 | export default CustomComponent
11 |
--------------------------------------------------------------------------------
/example/js-taro-react/src/pages/classb/index.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import { View } from '@styli/taro'
3 |
4 | export default class CustomComponent extends Component {
5 | render() {
6 | return export default class Component B
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/example/js-taro-react/src/pages/classc/index.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import { View } from '@styli/taro'
3 |
4 | export default class extends Component {
5 | render() {
6 | return export default class Component C
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/example/js-taro-react/src/pages/connectarrow/index.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { View } from '@styli/taro'
3 |
4 | const connect = () => {
5 | return CComponent => CComponent
6 | }
7 |
8 | const CustomComponent = () => {
9 | return export default connect arrow function Component
10 | }
11 |
12 | export default connect()(CustomComponent)
13 |
--------------------------------------------------------------------------------
/example/js-taro-react/src/pages/connectclass/index.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import { View } from '@styli/taro'
3 |
4 | const connect = () => {
5 | return CComponent => CComponent
6 | }
7 |
8 | class CustomComponent extends Component {
9 | render() {
10 | return export default connect class Component
11 | }
12 | }
13 |
14 | export default connect()(CustomComponent)
15 |
--------------------------------------------------------------------------------
/example/js-taro-react/src/pages/connectfunction/index.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { View } from '@styli/taro'
3 |
4 | const connect = () => {
5 | return CComponent => CComponent
6 | }
7 |
8 | function CustomComponent() {
9 | return export default connect function Component
10 | }
11 |
12 | export default connect()(CustomComponent)
13 |
--------------------------------------------------------------------------------
/example/js-taro-react/src/pages/functiona/index.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import { View } from '@styli/taro'
3 |
4 | function CustomComponent() {
5 | return (
6 |
7 | export default function Component A
8 |
9 |
10 |
11 | )
12 | }
13 |
14 | const CustomComponent2 = function() {
15 | return Component B
16 | }
17 |
18 | class CustomComponent3 extends Component {
19 | render() {
20 | return Component C
21 | }
22 | }
23 |
24 | export default CustomComponent
25 |
--------------------------------------------------------------------------------
/example/js-taro-react/src/pages/functionb/index.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import { View } from '@styli/taro'
3 |
4 | const CustomComponent2 = function() {
5 | return Component B
6 | }
7 |
8 | class CustomComponent3 extends Component {
9 | render() {
10 | return Component C
11 | }
12 | }
13 |
14 | export default function CustomComponent() {
15 | return (
16 |
17 | export default function Component B
18 |
19 |
20 |
21 | )
22 | }
23 |
--------------------------------------------------------------------------------
/example/js-taro-react/src/pages/functionc/index.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import { View } from '@styli/taro'
3 |
4 | const CustomComponent2 = function() {
5 | return Component B
6 | }
7 |
8 | class CustomComponent3 extends Component {
9 | render() {
10 | return Component C
11 | }
12 | }
13 |
14 | export default function() {
15 | return (
16 |
17 | export default function Component C
18 |
19 |
20 |
21 | )
22 | }
23 |
--------------------------------------------------------------------------------
/example/js-taro-react/src/pages/index/index.config.js:
--------------------------------------------------------------------------------
1 | export default {
2 | navigationBarTitleText: '首页'
3 | }
4 |
--------------------------------------------------------------------------------
/example/js-taro-react/src/pages/index/index.css:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xdoer/taro-inject-component-loader/30354b13da365152c67b6bf383eb2b32e23dbd27/example/js-taro-react/src/pages/index/index.css
--------------------------------------------------------------------------------
/example/js-taro-react/src/pages/index/index.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import { View } from '@styli/taro'
3 | import { navigateTo } from '@tarojs/taro'
4 | import './index.css'
5 |
6 | const data = [
7 | {
8 | title: 'Class A',
9 | path: '/pages/classa/index',
10 | },
11 | {
12 | title: 'Class B',
13 | path: '/pages/classb/index',
14 | },
15 | {
16 | title: 'Class C',
17 | path: '/pages/classc/index',
18 | },
19 | {
20 | title: 'Function A',
21 | path: '/pages/functiona/index',
22 | },
23 | {
24 | title: 'Function B',
25 | path: '/pages/functionb/index',
26 | },
27 | {
28 | title: 'Function C',
29 | path: '/pages/functionc/index',
30 | },
31 | {
32 | title: 'Arrow Function A',
33 | path: '/pages/arrowa/index',
34 | },
35 | {
36 | title: 'Arrow Function b',
37 | path: '/pages/arrowb/index',
38 | },
39 | {
40 | title: 'connect class',
41 | path: '/pages/connectclass/index',
42 | },
43 | {
44 | title: 'connect function',
45 | path: '/pages/connectfunction/index',
46 | },
47 | {
48 | title: 'connect arrow',
49 | path: '/pages/connectarrow/index',
50 | },
51 | ]
52 |
53 | export default class Index extends Component {
54 | componentWillMount() {}
55 |
56 | componentDidMount() {}
57 |
58 | componentWillUnmount() {}
59 |
60 | componentDidShow() {}
61 |
62 | componentDidHide() {}
63 |
64 | render() {
65 | return (
66 |
67 | {data.map((item, index) => (
68 | navigateTo({ url: item.path })}
81 | >
82 | {item.title}
83 |
84 | ))}
85 |
86 | )
87 | }
88 | }
89 |
--------------------------------------------------------------------------------
/example/ts-taro-react/.editorconfig:
--------------------------------------------------------------------------------
1 | # http://editorconfig.org
2 | root = true
3 |
4 | [*]
5 | indent_style = space
6 | indent_size = 2
7 | charset = utf-8
8 | trim_trailing_whitespace = true
9 | insert_final_newline = true
10 |
11 | [*.md]
12 | trim_trailing_whitespace = false
13 |
--------------------------------------------------------------------------------
/example/ts-taro-react/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "extends": ["taro/react"]
3 | }
4 |
--------------------------------------------------------------------------------
/example/ts-taro-react/.npmrc:
--------------------------------------------------------------------------------
1 | registry=https://registry.npm.taobao.org
2 | disturl=https://npm.taobao.org/dist
3 | sass_binary_site=https://npm.taobao.org/mirrors/node-sass/
4 | phantomjs_cdnurl=https://npm.taobao.org/mirrors/phantomjs/
5 | electron_mirror=https://npm.taobao.org/mirrors/electron/
6 | chromedriver_cdnurl=https://npm.taobao.org/mirrors/chromedriver
7 | operadriver_cdnurl=https://npm.taobao.org/mirrors/operadriver
8 | selenium_cdnurl=https://npm.taobao.org/mirrors/selenium
9 | node_inspector_cdnurl=https://npm.taobao.org/mirrors/node-inspector
10 | fsevents_binary_host_mirror=http://npm.taobao.org/mirrors/fsevents/
11 |
--------------------------------------------------------------------------------
/example/ts-taro-react/babel.config.js:
--------------------------------------------------------------------------------
1 | // babel-preset-taro 更多选项和默认值:
2 | // https://github.com/NervJS/taro/blob/next/packages/babel-preset-taro/README.md
3 | module.exports = {
4 | presets: [
5 | ['taro', {
6 | framework: 'react',
7 | ts: true
8 | }]
9 | ]
10 | }
11 |
--------------------------------------------------------------------------------
/example/ts-taro-react/config/dev.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | env: {
3 | NODE_ENV: '"development"'
4 | },
5 | defineConstants: {
6 | },
7 | mini: {},
8 | h5: {}
9 | }
10 |
--------------------------------------------------------------------------------
/example/ts-taro-react/config/index.js:
--------------------------------------------------------------------------------
1 | const path = require('path')
2 |
3 | const config = {
4 | projectName: 'taro-example',
5 | date: '2020-12-31',
6 | designWidth: 750,
7 | deviceRatio: {
8 | 640: 2.34 / 2,
9 | 750: 1,
10 | 828: 1.81 / 2,
11 | },
12 | sourceRoot: 'src',
13 | outputRoot: 'dist',
14 | plugins: [],
15 | defineConstants: {},
16 | copy: {
17 | patterns: [],
18 | options: {},
19 | },
20 | framework: 'react',
21 | alias: {
22 | '@components': path.resolve(__dirname, '..', 'src/components'),
23 | },
24 | mini: {
25 | postcss: {
26 | pxtransform: {
27 | enable: true,
28 | config: {},
29 | },
30 | url: {
31 | enable: true,
32 | config: {
33 | limit: 1024, // 设定转换尺寸上限
34 | },
35 | },
36 | cssModules: {
37 | enable: false, // 默认为 false,如需使用 css modules 功能,则设为 true
38 | config: {
39 | namingPattern: 'module', // 转换模式,取值为 global/module
40 | generateScopedName: '[name]__[local]___[hash:base64:5]',
41 | },
42 | },
43 | },
44 | webpackChain(chain) {
45 | chain.merge({
46 | module: {
47 | rule: {
48 | injectBaseComponentLoader: {
49 | test: /\.tsx$/,
50 | use: [
51 | {
52 | loader: path.resolve(__dirname, '../../../dist/index.js'),
53 | options: {
54 | importPath: '@components/BaseComponent',
55 | },
56 | },
57 | ],
58 | },
59 | },
60 | },
61 | })
62 | },
63 | },
64 | h5: {
65 | publicPath: '/',
66 | staticDirectory: 'static',
67 | postcss: {
68 | autoprefixer: {
69 | enable: true,
70 | config: {},
71 | },
72 | cssModules: {
73 | enable: false, // 默认为 false,如需使用 css modules 功能,则设为 true
74 | config: {
75 | namingPattern: 'module', // 转换模式,取值为 global/module
76 | generateScopedName: '[name]__[local]___[hash:base64:5]',
77 | },
78 | },
79 | },
80 | webpackChain(chain) {
81 | chain.merge({
82 | module: {
83 | rule: {
84 | injectBaseComponentLoader: {
85 | test: /\.tsx$/,
86 | use: [
87 | {
88 | loader: path.resolve(__dirname, '../../../dist/index.js'),
89 | options: {
90 | importSpecifier: '@components/BaseComponent',
91 | componentName: 'BaseComponent',
92 | isPage(filePath) {
93 | return /(package-.+\/)?pages\/.+\/index\.tsx$/.test(filePath)
94 | },
95 | },
96 | },
97 | ],
98 | },
99 | },
100 | },
101 | })
102 | },
103 | },
104 | }
105 |
106 | module.exports = function(merge) {
107 | if (process.env.NODE_ENV === 'development') {
108 | return merge({}, config, require('./dev'))
109 | }
110 | return merge({}, config, require('./prod'))
111 | }
112 |
--------------------------------------------------------------------------------
/example/ts-taro-react/config/prod.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | env: {
3 | NODE_ENV: '"production"'
4 | },
5 | defineConstants: {
6 | },
7 | mini: {},
8 | h5: {
9 | /**
10 | * 如果h5端编译后体积过大,可以使用webpack-bundle-analyzer插件对打包体积进行分析。
11 | * 参考代码如下:
12 | * webpackChain (chain) {
13 | * chain.plugin('analyzer')
14 | * .use(require('webpack-bundle-analyzer').BundleAnalyzerPlugin, [])
15 | * }
16 | */
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/example/ts-taro-react/global.d.ts:
--------------------------------------------------------------------------------
1 | declare module '*.png';
2 | declare module '*.gif';
3 | declare module '*.jpg';
4 | declare module '*.jpeg';
5 | declare module '*.svg';
6 | declare module '*.css';
7 | declare module '*.less';
8 | declare module '*.scss';
9 | declare module '*.sass';
10 | declare module '*.styl';
11 |
12 | declare namespace NodeJS {
13 | interface ProcessEnv {
14 | TARO_ENV: 'weapp' | 'swan' | 'alipay' | 'h5' | 'rn' | 'tt' | 'quickapp' | 'qq' | 'jd'
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/example/ts-taro-react/log.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { View } from '@styli/taro'
3 |
4 | export default function() {
5 | return (
6 |
7 |
8 | )
9 | }
10 | o'
11 |
12 | export default class Index extends Component {
13 |
14 | componentWillMount() { }
15 |
16 | componentDidMount() { }
17 |
18 | componentWillUnmount() { }
19 |
20 | componentDidShow() { }
21 |
22 | componentDidHide() { }
23 |
24 | render() {
25 | return (
26 |
27 |
28 | navigateTo({ url: '/package-test/pages/test/index' })}
38 | >
39 | 点击
40 |
41 |
42 |
43 | )
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/example/ts-taro-react/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "taro-example",
3 | "version": "1.0.0",
4 | "private": true,
5 | "description": "loader example",
6 | "templateInfo": {
7 | "name": "default",
8 | "typescript": true,
9 | "css": "sass"
10 | },
11 | "scripts": {
12 | "build:weapp": "taro build --type weapp",
13 | "build:swan": "taro build --type swan",
14 | "build:alipay": "taro build --type alipay",
15 | "build:tt": "taro build --type tt",
16 | "build:h5": "taro build --type h5",
17 | "build:rn": "taro build --type rn",
18 | "build:qq": "taro build --type qq",
19 | "build:jd": "taro build --type jd",
20 | "build:quickapp": "taro build --type quickapp",
21 | "dev:weapp": "npm run build:weapp -- --watch",
22 | "dev:swan": "npm run build:swan -- --watch",
23 | "dev:alipay": "npm run build:alipay -- --watch",
24 | "dev:tt": "npm run build:tt -- --watch",
25 | "dev:h5": "npm run build:h5 -- --watch",
26 | "dev:rn": "npm run build:rn -- --watch",
27 | "dev:qq": "npm run build:qq -- --watch",
28 | "dev:jd": "npm run build:jd -- --watch",
29 | "dev:quickapp": "npm run build:quickapp -- --watch"
30 | },
31 | "browserslist": [
32 | "last 3 versions",
33 | "Android >= 4.1",
34 | "ios >= 8"
35 | ],
36 | "author": "",
37 | "dependencies": {
38 | "@babel/runtime": "^7.7.7",
39 | "@styli/taro": "0.22.0",
40 | "@tarojs/components": "3.1.0-beta.4",
41 | "@tarojs/react": "3.1.0-beta.4",
42 | "@tarojs/runtime": "3.1.0-beta.4",
43 | "@tarojs/taro": "3.1.0-beta.4",
44 | "react": "^16.10.0",
45 | "react-dom": "^16.10.0"
46 | },
47 | "devDependencies": {
48 | "@babel/core": "^7.8.0",
49 | "@tarojs/mini-runner": "3.1.0-beta.4",
50 | "@tarojs/webpack-runner": "3.1.0-beta.4",
51 | "@types/react": "^16.0.0",
52 | "@types/webpack-env": "^1.13.6",
53 | "@typescript-eslint/eslint-plugin": "^2.x",
54 | "@typescript-eslint/parser": "^2.x",
55 | "babel-preset-taro": "3.1.0-beta.4",
56 | "eslint": "^6.8.0",
57 | "eslint-config-taro": "3.1.0-beta.4",
58 | "eslint-plugin-import": "^2.12.0",
59 | "eslint-plugin-react": "^7.8.2",
60 | "eslint-plugin-react-hooks": "^1.6.1",
61 | "stylelint": "9.3.0",
62 | "typescript": "^3.7.0"
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/example/ts-taro-react/project.config.json:
--------------------------------------------------------------------------------
1 | {
2 | "miniprogramRoot": "dist/",
3 | "projectname": "taro-example",
4 | "description": "loader example",
5 | "appid": "touristappid",
6 | "setting": {
7 | "urlCheck": true,
8 | "scopeDataCheck": false,
9 | "coverView": true,
10 | "es6": false,
11 | "postcss": true,
12 | "compileHotReLoad": false,
13 | "lazyloadPlaceholderEnable": false,
14 | "preloadBackgroundData": false,
15 | "minified": true,
16 | "autoAudits": false,
17 | "newFeature": false,
18 | "uglifyFileName": false,
19 | "uploadWithSourceMap": true,
20 | "useIsolateContext": true,
21 | "nodeModules": false,
22 | "enhance": true,
23 | "useMultiFrameRuntime": true,
24 | "useApiHook": true,
25 | "useApiHostProcess": true,
26 | "showShadowRootInWxmlPanel": true,
27 | "packNpmManually": false,
28 | "packNpmRelationList": [],
29 | "minifyWXSS": true
30 | },
31 | "compileType": "miniprogram",
32 | "condition": {}
33 | }
--------------------------------------------------------------------------------
/example/ts-taro-react/src/app.config.ts:
--------------------------------------------------------------------------------
1 | export default {
2 | pages: ['pages/index/index'],
3 | subPackages: [
4 | {
5 | root: 'package-test',
6 | name: 'test',
7 | pages: ['pages/test/index'],
8 | },
9 | ],
10 | window: {
11 | backgroundTextStyle: 'light',
12 | navigationBarBackgroundColor: '#fff',
13 | navigationBarTitleText: 'WeChat',
14 | navigationBarTextStyle: 'black'
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/example/ts-taro-react/src/app.scss:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xdoer/taro-inject-component-loader/30354b13da365152c67b6bf383eb2b32e23dbd27/example/ts-taro-react/src/app.scss
--------------------------------------------------------------------------------
/example/ts-taro-react/src/app.ts:
--------------------------------------------------------------------------------
1 | import { Component } from 'react'
2 | import './app.scss'
3 |
4 | class App extends Component {
5 |
6 | componentDidMount () {}
7 |
8 | componentDidShow () {}
9 |
10 | componentDidHide () {}
11 |
12 | componentDidCatchError () {}
13 |
14 | // this.props.children 是将要会渲染的页面
15 | render () {
16 | return this.props.children
17 | }
18 | }
19 |
20 | export default App
21 |
--------------------------------------------------------------------------------
/example/ts-taro-react/src/components/BaseComponent.tsx:
--------------------------------------------------------------------------------
1 | import { View } from '@styli/taro'
2 | import React, { memo } from 'react'
3 |
4 | export default memo(({ }) => {
5 | return (
6 |
7 | BaseComponent
8 | webpack inject
9 |
10 | )
11 | })
12 |
--------------------------------------------------------------------------------
/example/ts-taro-react/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/example/ts-taro-react/src/package-test/pages/test/index.config.ts:
--------------------------------------------------------------------------------
1 | export default {
2 | navigationBarTitleText: '分包子页面-函数式组件',
3 | }
4 |
--------------------------------------------------------------------------------
/example/ts-taro-react/src/package-test/pages/test/index.tsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import { View } from '@styli/taro'
3 |
4 |
5 | function a() {
6 | const Index222 = 1
7 |
8 | console.log(Index222)
9 | }
10 |
11 | const Index222 = class extends Component {
12 |
13 | render() {
14 | return (
15 |
16 | 哈哈哈哈哈哈哈
17 |
18 | )
19 | }
20 | }
21 |
22 | const X = () => 哈哈哈
23 |
24 | export default () => 哈哈哈
25 |
--------------------------------------------------------------------------------
/example/ts-taro-react/src/pages/index/index.config.ts:
--------------------------------------------------------------------------------
1 | export default {
2 | navigationBarTitleText: '主包-类组件'
3 | }
4 |
--------------------------------------------------------------------------------
/example/ts-taro-react/src/pages/index/index.scss:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xdoer/taro-inject-component-loader/30354b13da365152c67b6bf383eb2b32e23dbd27/example/ts-taro-react/src/pages/index/index.scss
--------------------------------------------------------------------------------
/example/ts-taro-react/src/pages/index/index.tsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import { View } from '@styli/taro'
3 | import './index.scss'
4 | import { navigateTo } from '@tarojs/taro'
5 |
6 | class A extends Component {
7 | render() {
8 | return 哈哈哈哈哈啊哈哈哈哈哈=========A组件
9 | }
10 | }
11 |
12 | export default class Index extends Component {
13 |
14 | componentWillMount() { }
15 |
16 | componentDidMount() { }
17 |
18 | componentWillUnmount() { }
19 |
20 | componentDidShow() { }
21 |
22 | componentDidHide() { }
23 |
24 | render() {
25 | return (
26 |
27 |
28 | navigateTo({ url: '/package-test/pages/test/index' })}
38 | >
39 | 点击
40 |
41 |
42 |
43 |
44 | )
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/example/ts-taro-react/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es2017",
4 | "module": "commonjs",
5 | "removeComments": false,
6 | "preserveConstEnums": true,
7 | "moduleResolution": "node",
8 | "experimentalDecorators": true,
9 | "noImplicitAny": false,
10 | "allowSyntheticDefaultImports": true,
11 | "outDir": "lib",
12 | "noUnusedLocals": true,
13 | "noUnusedParameters": true,
14 | "strictNullChecks": true,
15 | "sourceMap": true,
16 | "baseUrl": ".",
17 | "rootDir": ".",
18 | "jsx": "react",
19 | "jsxFactory": "React.createElement",
20 | "allowJs": true,
21 | "resolveJsonModule": true,
22 | "typeRoots": [
23 | "node_modules/@types",
24 | "global.d.ts"
25 | ],
26 | "paths": {
27 | "@components/*": ["src/components/*"]
28 | }
29 | },
30 | "exclude": [
31 | "node_modules",
32 | "dist"
33 | ],
34 | "compileOnSave": false
35 | }
36 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "2.1.1",
3 | "license": "MIT",
4 | "main": "dist/index.js",
5 | "typings": "dist/index.d.ts",
6 | "files": [
7 | "dist"
8 | ],
9 | "engines": {
10 | "node": ">=10"
11 | },
12 | "homepage": "https://github.com/xdoer/taro-inject-component-loader",
13 | "bugs": {
14 | "url": "https://github.com/xdoer/taro-inject-component-loader/issues",
15 | "email": "gotoanything@foxmail.com"
16 | },
17 | "keywords": [
18 | "taro",
19 | "react",
20 | "loader",
21 | "webpack",
22 | "inject"
23 | ],
24 | "scripts": {
25 | "start": "tsdx watch --format cjs",
26 | "build": "tsdx build --format cjs",
27 | "test": "tsdx test",
28 | "lint": "tsdx lint",
29 | "prepare": "tsdx build --format cjs",
30 | "size": "size-limit",
31 | "analyze": "size-limit --why"
32 | },
33 | "husky": {
34 | "hooks": {
35 | "pre-commit": "tsdx lint"
36 | }
37 | },
38 | "name": "taro-inject-component-loader",
39 | "author": "xdoer",
40 | "module": "dist/taro-inject-component-loader.esm.js",
41 | "size-limit": [
42 | {
43 | "path": "dist/taro-inject-component-loader.cjs.production.min.js",
44 | "limit": "10 KB"
45 | },
46 | {
47 | "path": "dist/taro-inject-component-loader.esm.js",
48 | "limit": "10 KB"
49 | }
50 | ],
51 | "peerDependencies": {
52 | "webpack": "^4.0.0 || ^5.0.0"
53 | },
54 | "dependencies": {
55 | "loader-utils": "^2.0.0",
56 | "schema-utils": "^3.0.0"
57 | },
58 | "devDependencies": {
59 | "@size-limit/preset-small-lib": "^4.9.1",
60 | "@types/loader-utils": "^2.0.1",
61 | "@types/schema-utils": "^2.4.0",
62 | "husky": "^4.3.6",
63 | "size-limit": "^4.9.1",
64 | "tsdx": "^0.14.1",
65 | "tslib": "^2.0.3",
66 | "typescript": "^4.1.3"
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
1 | import generate from '@babel/generator'
2 | import traverse from '@babel/traverse'
3 | import utils from '@babel/types'
4 | import { parse } from '@babel/parser'
5 | import { getOptions } from 'loader-utils'
6 | import { validate } from 'schema-utils'
7 |
8 | const schema = {
9 | type: 'object',
10 | properties: {
11 | importPath: {
12 | type: 'string',
13 | },
14 | logError: {
15 | type: 'boolean'
16 | },
17 | isPage: {
18 | instanceof: 'Function',
19 | },
20 | },
21 | additionalProperties: false,
22 | }
23 |
24 | export default function (source: string) {
25 | // @ts-ignore
26 | const webpackEnv = this
27 |
28 | const options = getOptions(webpackEnv)
29 |
30 | validate(schema as any, options, { name: 'taro-inject-component-loader' })
31 |
32 | const { importPath = '', componentName = 'WebpackInjected', logError = true, isPage = defaultJudgePage } = options || {}
33 |
34 | // 获取原始文件地址
35 | const filePath = webpackEnv.resourcePath
36 |
37 | if (typeof isPage === 'function' && isPage(filePath)) {
38 | // 生成 AST
39 | const ast: any = parse(source, {
40 | sourceType: 'module',
41 | plugins: ['jsx', 'typescript', 'classProperties'],
42 | })
43 |
44 | // 如果有导入申明,则默认表示已手动导入了组件
45 | let insert = false
46 |
47 | // 保存所有顶层的声明
48 | const declarations = new Map()
49 |
50 | traverse(ast, {
51 | // 查找是否有导入
52 | ImportDeclaration(path) {
53 | if (path.node.source.value === importPath) {
54 | insert = true
55 | }
56 | },
57 |
58 | // 收集页面文件里的所有申明
59 | // 类组件
60 | ClassDeclaration(path) {
61 | // 如果不是顶层的申明,则直接返回
62 | if (path.parent.type !== 'Program') return
63 |
64 | const type = path.node.type
65 | const name = path.node.id.name
66 | declarations.set(name, type)
67 | },
68 |
69 | // 函数申明
70 | FunctionDeclaration(path) {
71 | // 如果不是顶层的申明,则直接返回
72 | if (path.parent.type !== 'Program') return
73 |
74 | const type = path.node.type
75 | const name = path.node.id?.name
76 | if (!name) return
77 |
78 | declarations.set(name, type)
79 | },
80 |
81 | // 表达式申明
82 | VariableDeclaration(path) {
83 | // 如果不是顶层的申明,则直接返回
84 | if (path.parent.type !== 'Program') return
85 |
86 | path.node.declarations.forEach((declaration: any) => {
87 |
88 | // const a = () => {}
89 | if (declaration.init?.type === 'ArrowFunctionExpression') {
90 | const type = declaration.init?.type
91 | const name = declaration.id?.name
92 | declarations.set(name, type)
93 | }
94 |
95 | // const a = function(){}
96 | if (declaration.init?.type === 'FunctionExpression') {
97 | const type = declaration.init.type
98 | const name = declaration.id.name
99 | declarations.set(name, type)
100 | }
101 |
102 | // const a = class {}
103 | if (declaration.init?.type === 'ClassExpression') {
104 | const type = declaration.init.type
105 | const name = declaration.id.name
106 | declarations.set(name, type)
107 | }
108 | })
109 | },
110 | })
111 |
112 | if (!insert) {
113 | // 记录组件插入状态
114 | const state = {
115 | importedDeclaration: false,
116 | importedComponent: false,
117 | }
118 |
119 | traverse(ast, {
120 | // 添加申明
121 | ImportDeclaration(path) {
122 | if (!state.importedDeclaration) {
123 | state.importedDeclaration = true
124 | path.insertBefore(
125 | utils.importDeclaration(
126 | [
127 | utils.importDefaultSpecifier(utils.identifier('' + componentName)),
128 | ],
129 | utils.stringLiteral('' + importPath),
130 | ),
131 | )
132 | }
133 | },
134 |
135 | // 默认导出的为页面组件
136 | ExportDefaultDeclaration(path) {
137 |
138 | // 如果默认导出的是函数
139 | if (path.node.declaration.type === 'FunctionDeclaration') {
140 | const mainFnBody = path.node.declaration.body.body
141 | const length = mainFnBody.length
142 | const last = mainFnBody[length - 1]
143 | insertComponent(last, '' + componentName, state)
144 | }
145 |
146 | // 默认导出箭头函数
147 | if (path.node.declaration.type === 'ArrowFunctionExpression') {
148 | // export default () => { return }
149 | if (path.node.declaration.body.type === 'BlockStatement') {
150 | const mainFnBody = path.node.declaration.body.body
151 | const length = mainFnBody.length
152 | const last = mainFnBody[length - 1]
153 | insertComponent(last, '' + componentName, state)
154 | } else {
155 | // export default () =>
156 | insertComponent(path.node.declaration.body, '' + componentName, state)
157 | }
158 | }
159 |
160 | // 默认导出类
161 | if (path.node.declaration.type === 'ClassDeclaration') {
162 | traverse(path.node, {
163 | ClassMethod(path) {
164 | if ((path.node.key as any).name === 'render') {
165 | const body = path.node.body.body || []
166 | const last = body[body.length - 1]
167 | insertComponent(last, '' + componentName, state)
168 | return
169 | }
170 | },
171 | }, path.scope, path)
172 | }
173 |
174 | // 如果默认导出的是一个申明
175 | if (path.node.declaration.type === "Identifier") {
176 | const name = path.node.declaration.name
177 | const componentType = declarations.get(name)
178 |
179 | traverse(path.parent, {
180 | FunctionDeclaration(path) {
181 | if (path.node.id?.name !== name) return
182 | const mainFnBody = path.node?.body?.body
183 | const length = mainFnBody.length
184 | const last = mainFnBody[length - 1]
185 | insertComponent(last, '' + componentName, state)
186 | },
187 | ClassDeclaration(path) {
188 | if (path.node.id.name !== name) return
189 | traverse(path.node, {
190 | ClassMethod(path) {
191 | if ((path.node.key as any)?.name !== 'render') return
192 | const body = path.node.body.body || []
193 | const last = body[body.length - 1]
194 | insertComponent(last, '' + componentName, state)
195 | },
196 | }, path.scope, path)
197 | },
198 | VariableDeclarator(path) {
199 | if (path.node.id.type !== 'Identifier') return
200 | if (path.node.id.name !== name) return
201 | if (!path.node.init) return
202 |
203 | if (path.node.init.type !== componentType) return
204 |
205 | if (path.node.init.type === 'FunctionExpression') {
206 | const mainFnBody = path.node.init.body.body
207 | const length = mainFnBody.length
208 | const last = mainFnBody[length - 1]
209 | insertComponent(last, '' + componentName, state)
210 | }
211 |
212 | if (path.node.init.type === 'ClassExpression') {
213 | traverse(path.node, {
214 | ClassMethod(path) {
215 | if ((path.node.key as any).name !== 'render') return
216 | const body = path.node.body.body || []
217 | const last = body[body.length - 1]
218 | insertComponent(last, '' + componentName, state)
219 | },
220 | }, path.scope, path)
221 | }
222 |
223 | if (path.node.init.type === 'ArrowFunctionExpression') {
224 | // const A = () => {}
225 | // export default A
226 | if (path.node.init.body.type == 'BlockStatement') {
227 | const mainFnBody = path.node.init.body.body
228 | const length = mainFnBody.length
229 | const last = mainFnBody[length - 1]
230 | insertComponent(last, '' + componentName, state)
231 | } else {
232 | // const A = () =>
233 | // export default A
234 | insertComponent(path.node.init.body, '' + componentName, state)
235 | }
236 | }
237 | }
238 | })
239 | }
240 | },
241 | })
242 |
243 | if (!state.importedComponent && logError) {
244 | webpackEnv.emitWarning(`页面: ${filePath} 注入组件失败,建议手动引入组件。组件注入限制请查阅: https://github.com/xdoer/taro-inject-component-loader`)
245 |
246 | }
247 | if (!state.importedDeclaration && logError) {
248 | webpackEnv.emitWarning(`页面: ${filePath} 注入导入申明失败,建议手动引入组件。组件注入限制请查阅: https://github.com/xdoer/taro-inject-component-loader`)
249 | }
250 |
251 | source = generate(ast).code
252 | }
253 | }
254 |
255 | return source
256 | }
257 |
258 |
259 | function createElement(name: string) {
260 | const reactIdentifier = utils.identifier('React')
261 | const createElementIdentifier = utils.identifier('createElement')
262 | const callee = utils.memberExpression(reactIdentifier, createElementIdentifier)
263 | return utils.callExpression(callee, [utils.identifier(name)])
264 | }
265 |
266 | function createJSX(name: string) {
267 | return utils.jSXElement(
268 | utils.jSXOpeningElement(utils.jsxIdentifier('' + name), [], true),
269 | null,
270 | [],
271 | true,
272 | )
273 | }
274 |
275 | function wrapJSX(name: string, temp: any) {
276 | return utils.jSXElement(
277 | utils.jSXOpeningElement(utils.jsxIdentifier('' + name), [], false),
278 | utils.jSXClosingElement(utils.jsxIdentifier('' + name)),
279 | [temp],
280 | false,
281 | )
282 | }
283 |
284 | function insertComponent(node: any, componentName: string, state: any) {
285 | if (node?.type === 'ReturnStatement') {
286 | // createElement
287 | if (node.argument?.callee?.property?.name === 'createElement' && !state.importedComponent) {
288 | state.importedComponent = true
289 | const reactCreateArguments = node.argument.arguments
290 | reactCreateArguments.push(createElement(componentName))
291 | }
292 | // JSX
293 | if (node.argument?.type === 'JSXElement' && !state.importedComponent) {
294 | state.importedComponent = true
295 | // @caiminchao 开源方案,只修改了这个方法,就是将node.argument塞入import的组件children的位置
296 | const temp = node.argument;
297 | node.argument = wrapJSX(componentName, temp);
298 | }
299 | }
300 | if (node.type === 'JSXElement' && !state.importedComponent) {
301 | node.children.push(createJSX(componentName))
302 | }
303 | }
304 |
305 | function defaultJudgePage(filePath: string) {
306 | // 兼容 windows 路径
307 | const formatFilePath = filePath.replace(/\\/g, '/')
308 | return /(package-.+\/)?pages\/[A-Za-z0-9-]+\/index\.[tj]sx$/.test(formatFilePath)
309 | }
310 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | // see https://www.typescriptlang.org/tsconfig to better understand tsconfigs
3 | "include": ["src", "types"],
4 | "compilerOptions": {
5 | "module": "esnext",
6 | "lib": ["dom", "esnext"],
7 | "importHelpers": true,
8 | // output .d.ts declaration files for consumers
9 | "declaration": true,
10 | // output .js.map sourcemap files for consumers
11 | "sourceMap": true,
12 | // match output dir to input dir. e.g. dist/index instead of dist/src/index
13 | "rootDir": "./src",
14 | // stricter type-checking for stronger correctness. Recommended by TS
15 | "strict": true,
16 | // linter checks for common issues
17 | "noImplicitReturns": true,
18 | "noFallthroughCasesInSwitch": true,
19 | // noUnused* overlap with @typescript-eslint/no-unused-vars, can disable if duplicative
20 | "noUnusedLocals": true,
21 | "noUnusedParameters": true,
22 | // use Node's module resolution algorithm, instead of the legacy TS one
23 | "moduleResolution": "node",
24 | // transpile JSX to React.createElement
25 | "jsx": "react",
26 | // interop between ESM and CJS modules. Recommended by TS
27 | "esModuleInterop": true,
28 | // significant perf increase by skipping checking .d.ts files, particularly those in node_modules. Recommended by TS
29 | "skipLibCheck": true,
30 | // error out if import and file system have a casing mismatch. Recommended by TS
31 | "forceConsistentCasingInFileNames": true,
32 | // `tsdx build` ignores this option, but it is commonly used when type-checking separately with `tsc`
33 | "noEmit": true,
34 | }
35 | }
36 |
--------------------------------------------------------------------------------