@tmsfe/tmskit 0.0.5-beta.5 → 0.0.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/README.md +27 -25
  2. package/dist/index.cjs.js +779 -664
  3. package/main.js +3 -3
  4. package/package.json +75 -69
  5. package/src/config/constant.js +71 -70
  6. package/src/config/defaultTmsConfig.js +16 -16
  7. package/src/entry.js +60 -60
  8. package/src/gulp/build.js +5 -8
  9. package/src/gulp/compile.js +81 -87
  10. package/src/gulp/dev.js +102 -108
  11. package/src/gulp/plugins/less.js +116 -116
  12. package/src/gulp/plugins/mpCommonDep.js +131 -131
  13. package/src/gulp/plugins/mpJsonDep.js +108 -108
  14. package/src/gulp/plugins/mpWxmlDep.js +194 -194
  15. package/src/gulp/plugins/postcss-font-base64.js +72 -72
  16. package/src/gulp/{replaceEnv.js → plugins/replaceEnv.js} +29 -29
  17. package/src/gulp/plugins/utils/pluginError.js +25 -25
  18. package/src/index.js +62 -62
  19. package/src/init.js +33 -33
  20. package/src/scripts/create/ask.js +63 -63
  21. package/src/scripts/create/generator.js +25 -25
  22. package/src/scripts/create/ignoreFiles.js +7 -7
  23. package/src/scripts/create/index.js +72 -72
  24. package/src/scripts/create/render.js +19 -19
  25. package/src/scripts/run/build/index.js +17 -16
  26. package/src/scripts/run/dev/index.js +84 -67
  27. package/src/scripts/run/index.js +68 -63
  28. package/src/scripts/run/init/index.js +87 -80
  29. package/src/scripts/run/install/index.js +29 -31
  30. package/src/utils/buildAppJson.js +221 -200
  31. package/src/utils/checkDependencies.js +77 -77
  32. package/src/utils/cliUtils.js +35 -35
  33. package/src/utils/cloneModules.js +116 -91
  34. package/src/utils/findCssImport.js +30 -30
  35. package/src/utils/global.js +36 -36
  36. package/src/utils/handleError.js +16 -0
  37. package/src/utils/io.js +106 -106
  38. package/src/utils/log.js +44 -44
  39. package/src/utils/mpCiUtils.js +73 -74
  40. package/src/utils/npmUtils.js +166 -148
  41. package/src/utils/tkitUtils.js +158 -84
  42. package/src/utils/widgets.js +167 -173
@@ -1,67 +1,84 @@
1
- const gulpDev = require('../../../gulp/dev');
2
- const fs = require('fs');
3
- const { resolve } = require('../../../utils/widgets');
4
- const init = require('../init/index');
5
- const { DEFAULT_MODULE_DIR } = require('../../../config/constant');
6
- const { tmsModulesMergeLocalModuleCfg } = require('../../../utils/tkitUtils');
7
- const { checkDependencies } = require('../../../utils/checkDependencies');
8
- const { fail } = require('../../../utils/log');
9
-
10
- function isInit(tmsConfig, targetModules, contextDir) {
11
- // 判断是否存在dist目录
12
- if (!fs.existsSync(contextDir)) {
13
- return true;
14
- }
15
- // 判断dist是否存在node_modules
16
- if (!fs.existsSync(`${contextDir}/node_modules`)) {
17
- return true;
18
- }
19
-
20
- // 判断dist是否存在miniprogram_npm
21
- if (!fs.existsSync(`${contextDir}/miniprogram_npm`)) {
22
- return true;
23
- }
24
-
25
- // 判断dist是否存在app.json
26
- if (!fs.existsSync(`${contextDir}/app.json`)) {
27
- return true;
28
- }
29
-
30
- // 判断\源码\dist\是否存在用户指定的模块
31
- for (const item of targetModules) {
32
- // 此模块没有root字段(原因:没有merge到module.config.json的配置项。第三方模块的代码可能还没有下载)
33
- if (!item.root) {
34
- return true;
35
- }
36
- // 判断编译目录是否有该模块
37
- if (!fs.existsSync(`${contextDir}/${item.root}`)) {
38
- return true;
39
- }
40
- // 判断源码目录是否有该模块
41
- if (item.path && !fs.existsSync(resolve(item.path))) {
42
- fail(`${item.path}模块代码路径不存在, 请检查tms.config.js的${item.name}模块的path`);
43
- process.exit(1);
44
- }
45
- }
46
-
47
- // 判断package.json的版本是否有新的版本
48
- return checkDependencies(targetModules, resolve('./'), tmsConfig.outputDir);
49
- }
50
-
51
- async function dev(tmsConfig, targetModules, env) {
52
- // tms.config.js的modules 合并 module.config.json的配置项
53
- let newModules = tmsModulesMergeLocalModuleCfg(targetModules, tmsConfig.appName, DEFAULT_MODULE_DIR);
54
-
55
- // 判断是否进行init命令
56
- if (isInit(tmsConfig, newModules, resolve('dist'))) {
57
- // init函数会将 最新的tms.config.js的modules 合并 module.config.json的配置项 返回,不需要再做重复工作
58
- const initData = await init(tmsConfig, targetModules);
59
- newModules = initData.targetModules;
60
- }
61
-
62
- console.log('当前dev启动的有效模块', newModules.map(item => item.name));
63
- gulpDev(tmsConfig, newModules, env);
64
- }
65
-
66
- module.exports = dev;
67
-
1
+ const gulpDev = require('../../../gulp/dev');
2
+ const fs = require('fs');
3
+ const { resolve } = require('../../../utils/widgets');
4
+ const init = require('../init/index');
5
+ const { MODULE_CONFIG_FILENAME } = require('../../../config/constant');
6
+ const { tmsModulesMergeLocalModuleCfg } = require('../../../utils/tkitUtils');
7
+ const { checkDependencies } = require('../../../utils/checkDependencies');
8
+ const { fail } = require('../../../utils/log');
9
+ const shelljs = require('shelljs');
10
+ // const io = require('../../../utils/io');
11
+
12
+ function isInit(tmsConfig, targetModules, contextDir, isDev) {
13
+ // 判断是否存在dist目录
14
+ if (!fs.existsSync(contextDir)) {
15
+ return true;
16
+ }
17
+ // 判断dist是否存在node_modules
18
+ if (!fs.existsSync(`${contextDir}/node_modules`)) {
19
+ return true;
20
+ }
21
+
22
+ // 判断dist是否存在miniprogram_npm
23
+ if (!fs.existsSync(`${contextDir}/miniprogram_npm`)) {
24
+ return true;
25
+ }
26
+
27
+ // 判断dist是否存在app.json
28
+ if (!fs.existsSync(`${contextDir}/app.json`)) {
29
+ return true;
30
+ }
31
+
32
+ // 判断\源码\dist\是否存在用户指定的模块
33
+ for (const item of targetModules) {
34
+ // 此模块没有root字段(原因:没有merge到module.config.json的配置项。第三方模块的代码可能还没有下载)
35
+ if (!item.root) {
36
+ return true;
37
+ }
38
+ // 判断dist目录是否有该模块
39
+ if (!fs.existsSync(`${contextDir}/${item.root}/${MODULE_CONFIG_FILENAME}`)) {
40
+ return true;
41
+ }
42
+ // 判断源码目录是否有该模块
43
+ if (item.path && !fs.existsSync(resolve(item.path))) {
44
+ fail(`${item.path}模块代码路径不存在, 请检查tms.config.js的${item.name}模块的path`);
45
+ process.exit(1);
46
+ }
47
+ }
48
+
49
+ // 判断package.json的版本是否有新的版本
50
+ return checkDependencies(targetModules, resolve('./'), tmsConfig.outputDir, isDev);
51
+ }
52
+
53
+ // 用户编译分包时,需要将dist中其他分包(主包不能删除)的内容删除,否则其他分包的内容混入到主包(导致主包的体积超2M)
54
+ function delOtherModule(tmsConfig, targetModules) {
55
+ const modules = tmsModulesMergeLocalModuleCfg(tmsConfig.modules, tmsConfig.appName);
56
+ const targetModulesName = targetModules.map(item => item.name);
57
+ modules.forEach((item) => {
58
+ if (item.root && targetModulesName.indexOf(item.name) === -1) {
59
+ const moduleRootDir = resolve(`dist/${item.root}`);
60
+ shelljs.rm('-rf', `${moduleRootDir}/*`, { silent: true });
61
+ // 解决微信开发者工具(dist/app.json: ["subpackages"][0]["root"] 字段需为 目录)错误 - 提前创建该目录
62
+ // io.ensureDirExist(moduleRootDir);
63
+ }
64
+ });
65
+ }
66
+
67
+ async function dev(tmsConfig, targetModules, env) {
68
+ let newModules = targetModules;
69
+ const isDev = true;
70
+
71
+ // 判断是否进行init命令
72
+ if (isInit(tmsConfig, targetModules, resolve('dist'), isDev)) {
73
+ // init函数 下载第三方代码后,会将最新的tms.config.js的modules 合并 module.config.json的配置项返回
74
+ const initData = await init(tmsConfig, newModules, isDev);
75
+ newModules = initData.targetModules;
76
+ }
77
+
78
+ console.log('当前dev启动的有效模块', newModules.map(item => item.name).sort());
79
+ delOtherModule(tmsConfig, newModules);
80
+ gulpDev(tmsConfig, newModules, env);
81
+ }
82
+
83
+ module.exports = dev;
84
+
@@ -1,63 +1,68 @@
1
- const init = require('./init/index');
2
- const dev = require('./dev/index');
3
- const build = require('./build/index');
4
- const install = require('./install/index');
5
- const { createTask } = require('../../utils/widgets');
6
- const { MODE } = require('../../config/constant');
7
- const { readTmsConfig, checkModules } = require('../../utils/tkitUtils');
8
-
9
- const handleModulesArg = (cmd) => {
10
- // 主模块开发
11
- if (cmd.module === MODE.main) {
12
- return MODE.main;
13
- }
14
- // 单模块 多模块开发
15
- const { argv } = process;
16
- const reset = argv.indexOf('-m') > -1 ? argv.slice(argv.indexOf('-m') + 1) : [];
17
- if (cmd.module) {
18
- return [
19
- cmd.module,
20
- ...reset,
21
- ];
22
- }
23
- // 全量模块
24
- return MODE.all;
25
- };
26
-
27
- async function run(commandName, cmd) {
28
- const moduleArg = handleModulesArg(cmd);
29
- const { env } = cmd;
30
-
31
- const tmsConfig = await createTask(readTmsConfig, '开始读取脚手架的配置项', '读取脚手架的配置项完成')(env);
32
-
33
- let modules;
34
- // 主模块
35
- if (moduleArg === MODE.main) {
36
- modules = checkModules(tmsConfig, [...new Set([...tmsConfig.mainPackages])]);;
37
- } else if (moduleArg === MODE.all) {
38
- // 全量模块
39
- modules = tmsConfig.modules;
40
- } else {
41
- // 检查用户输入modules的有效性
42
- modules = checkModules(tmsConfig, [...new Set([...tmsConfig.mainPackages, ...moduleArg])]);;
43
- }
44
-
45
- switch (commandName) {
46
- case 'init':
47
- init(tmsConfig, modules, env);
48
- return;
49
- case 'dev':
50
- dev(tmsConfig, modules, env);
51
- return;
52
- case 'install':
53
- install(tmsConfig, modules, env);
54
- return;
55
- case 'build':
56
- build(tmsConfig, modules, env);
57
- return;
58
- default:
59
- return;
60
- }
61
- }
62
-
63
- module.exports = run;
1
+ const init = require('./init/index');
2
+ const dev = require('./dev/index');
3
+ const build = require('./build/index');
4
+ const install = require('./install/index');
5
+ const { createTask } = require('../../utils/widgets');
6
+ const { MODE } = require('../../config/constant');
7
+ const { readTmsConfig, readTmsPrivateCf, checkModules } = require('../../utils/tkitUtils');
8
+ const { tmsModulesMergeLocalModuleCfg, subModulesMergeDepModules } = require('../../utils/tkitUtils');
9
+
10
+ const handleModulesArg = (cmd, tmsPrivateCg) => {
11
+ // 单模块或多模块开发-用户通过脚手架参数指定的模块
12
+ if (cmd.module) {
13
+ const { argv } = process;
14
+ const reset = argv.indexOf('-m') > -1 ? argv.slice(argv.indexOf('-m') + 1) : [];
15
+ return [
16
+ cmd.module,
17
+ ...reset,
18
+ ];
19
+ }
20
+ // 单模块或多模块开发-用户在tms.private.js指定的模块
21
+ if (tmsPrivateCg?.modules && tmsPrivateCg?.modules.length > 0) {
22
+ return tmsPrivateCg?.modules;
23
+ }
24
+ // 全量模块
25
+ return MODE.all;
26
+ };
27
+
28
+ async function run(commandName, cmd) {
29
+ // 用户本地的私有项目配置(用来配置环境\模块信息\账号信息)
30
+ const tmsPrivateCg = readTmsPrivateCf();
31
+ // 处理module参数
32
+ const moduleArg = handleModulesArg(cmd, tmsPrivateCg);
33
+ const { env = tmsPrivateCg?.env } = cmd;
34
+ const tmsConfig = await createTask(readTmsConfig, '开始读取脚手架的配置项', '读取脚手架的配置项完成')(env);
35
+
36
+ let modules;
37
+ if (moduleArg === MODE.all) {
38
+ // 全量模块
39
+ modules = tmsConfig.modules;
40
+ } else {
41
+ // 检查用户输入modules的有效性
42
+ modules = checkModules(tmsConfig, [...new Set([...tmsConfig.mainPackages, ...moduleArg])]);
43
+ }
44
+
45
+ // tms.config.js的modules 合并 module.config.json的配置项
46
+ let newModules = tmsModulesMergeLocalModuleCfg(modules, tmsConfig.appName);
47
+ // A分包依赖了B分包的代码, merge B分包进行编译;
48
+ newModules = subModulesMergeDepModules(tmsConfig, newModules);
49
+
50
+ switch (commandName) {
51
+ case 'init':
52
+ init(tmsConfig, newModules, env);
53
+ return;
54
+ case 'dev':
55
+ dev(tmsConfig, newModules, env);
56
+ return;
57
+ case 'install':
58
+ install(tmsConfig, newModules, env);
59
+ return;
60
+ case 'build':
61
+ build(tmsConfig, newModules, env);
62
+ return;
63
+ default:
64
+ return;
65
+ }
66
+ }
67
+
68
+ module.exports = run;
@@ -1,80 +1,87 @@
1
- const shelljs = require('shelljs');
2
- const fs = require('fs');
3
- const io = require('../../../utils/io');
4
- const { resolve, createTask } = require('../../../utils/widgets');
5
- const { buildOutputAppJson } = require('../../../utils/buildAppJson');
6
- const { MODULE_CODE_DIR, DEFAULT_COPY_CONFIG, DEFAULT_MODULE_DIR } = require('../../../config/constant');
7
- const { cloneModules } = require('../../../utils/cloneModules');
8
- const { tmsModulesMergeLocalModuleCfg } = require('../../../utils/tkitUtils');
9
- const { fail } = require('../../../utils/log');
10
- const install = require('../install');
11
-
12
- /**
13
- * 拷贝相关配置文件到编译输出目录
14
- * @param { object } tmsConfig
15
- * @param { array } modules
16
- * @param { array } defaultFiles 默认需要拷贝的配置项
17
- * @returns
18
- */
19
- const cpFilesToOutput = function (tmsConfig, targetModules, defaultFiles) {
20
- const outputDir = resolve(tmsConfig.outputDir);
21
- io.ensureDirExist(outputDir);
22
- defaultFiles.forEach((item) => {
23
- if (fs.existsSync(resolve(item))) {
24
- shelljs.cp('-rf', resolve(item), resolve(tmsConfig.outputDir, item));
25
- }
26
- });
27
-
28
- // 拷贝模块的package.json到编译输出目录
29
- targetModules.forEach((item) => {
30
- const outputModuleDir = resolve(`${tmsConfig.outputDir}/${item.root}`);
31
- if (!fs.existsSync(resolve(item.path))) {
32
- fail(`${item.path}模块代码路径不存在, 请检查tms.config.js的${item.name}模块的path`);
33
- process.exit(1);
34
- }
35
- io.ensureDirExist(outputModuleDir);
36
- const modulePackagePath = resolve(item.path, 'package.json');
37
- if (fs.existsSync(modulePackagePath)) shelljs.cp('-Rf', modulePackagePath, outputModuleDir);
38
- });
39
- };
40
-
41
- async function task(tmsConfig, targetModules) {
42
- // 下载和移动代码
43
- await createTask(
44
- cloneModules,
45
- '开始下载模块代码完成',
46
- '下载模块代码码完成',
47
- )(MODULE_CODE_DIR, resolve('./'), targetModules);
48
-
49
- // tms.config.js的modules 合并 module.config.json的配置项
50
- const newModules = tmsModulesMergeLocalModuleCfg(targetModules, tmsConfig.appName, DEFAULT_MODULE_DIR);
51
- console.log('当前init的有效模块', newModules.map(item => item.name));
52
-
53
- // 拷贝相关配置文件到输出目录
54
- await createTask(
55
- cpFilesToOutput,
56
- '开始拷贝文件到编译输出目录',
57
- '拷贝文件到编译输出目录完成',
58
- )(tmsConfig, newModules, DEFAULT_COPY_CONFIG);
59
-
60
- // install
61
- await install(tmsConfig, newModules, false);
62
-
63
- // 动态生成编译后的app.json;
64
- await createTask(
65
- buildOutputAppJson,
66
- '开始生成编译后的app.json',
67
- '生成编译后的app.json完成',
68
- )(tmsConfig, newModules);
69
-
70
- return newModules;
71
- }
72
- async function init(tmsConfig, targetModules) {
73
- const newModules = await task(tmsConfig, targetModules);
74
-
75
- return {
76
- targetModules: newModules,
77
- };
78
- }
79
-
80
- module.exports = init;
1
+ const shelljs = require('shelljs');
2
+ const fs = require('fs');
3
+ const io = require('../../../utils/io');
4
+ const { resolve, createTask } = require('../../../utils/widgets');
5
+ const { buildOutputAppJson } = require('../../../utils/buildAppJson');
6
+ const { MODULE_CODE_DIR, DEFAULT_COPY_CONFIG } = require('../../../config/constant');
7
+ const { cloneModules } = require('../../../utils/cloneModules');
8
+ const { tmsModulesMergeLocalModuleCfg, subModulesMergeDepModules } = require('../../../utils/tkitUtils');
9
+ const { fail } = require('../../../utils/log');
10
+ const install = require('../install');
11
+
12
+ /**
13
+ * 拷贝相关配置文件到编译输出目录
14
+ * @param { object } tmsConfig
15
+ * @param { array } modules
16
+ * @param { array } defaultFiles 默认需要拷贝的配置项
17
+ * @returns
18
+ */
19
+ const cpFilesToOutput = function (tmsConfig, targetModules, defaultFiles) {
20
+ const outputDir = resolve(tmsConfig.outputDir);
21
+ io.ensureDirExist(outputDir);
22
+ defaultFiles.forEach((item) => {
23
+ if (fs.existsSync(resolve(item))) {
24
+ shelljs.cp('-rf', resolve(item), resolve(tmsConfig.outputDir, item));
25
+ }
26
+ });
27
+
28
+ // 拷贝模块的package.json到编译输出目录
29
+ targetModules.forEach((item) => {
30
+ const outputModuleDir = resolve(`${tmsConfig.outputDir}/${item.root}`);
31
+ if (!fs.existsSync(resolve(item.path))) {
32
+ fail(`${item.path}模块代码路径不存在, 请检查tms.config.js的${item.name}模块的path`);
33
+ process.exit(1);
34
+ }
35
+ io.ensureDirExist(outputModuleDir);
36
+ const modulePackagePath = resolve(item.path, 'package.json');
37
+ if (fs.existsSync(modulePackagePath)) shelljs.cp('-Rf', modulePackagePath, outputModuleDir);
38
+ });
39
+ };
40
+
41
+ async function task(tmsConfig, targetModules, isDev) {
42
+ // 下载和移动代码
43
+ await createTask(
44
+ cloneModules,
45
+ '开始下载模块代码完成',
46
+ '下载模块代码码完成',
47
+ )(MODULE_CODE_DIR, resolve('./'), targetModules, isDev);
48
+
49
+ // tms.config.js的modules 合并 module.config.json的配置项
50
+ let newModules = tmsModulesMergeLocalModuleCfg(targetModules, tmsConfig.appName);
51
+ // A分包依赖了B分包的代码, merge B分包进行编译;
52
+ newModules = subModulesMergeDepModules(tmsConfig, newModules);
53
+
54
+ // 拷贝相关配置文件到输出目录
55
+ await createTask(
56
+ cpFilesToOutput,
57
+ '开始拷贝文件到编译输出目录',
58
+ '拷贝文件到编译输出目录完成',
59
+ )(tmsConfig, newModules, DEFAULT_COPY_CONFIG);
60
+
61
+ // install
62
+ await install(tmsConfig, newModules, false);
63
+
64
+ // 动态生成编译后的app.json;
65
+ await createTask(
66
+ buildOutputAppJson,
67
+ '开始生成编译后的app.json',
68
+ '生成编译后的app.json完成',
69
+ )(tmsConfig, newModules, isDev);
70
+
71
+ return newModules;
72
+ }
73
+ async function init(tmsConfig, targetModules, isDev) {
74
+ try {
75
+ const newModules = await task(tmsConfig, targetModules, isDev);
76
+
77
+ return {
78
+ targetModules: newModules,
79
+ };
80
+ } catch (error) {
81
+ const errMsg = typeof error === 'object' ? error.message : error;
82
+ fail(`init流程出现错误${errMsg}`);
83
+ process.exit(1);
84
+ }
85
+ }
86
+
87
+ module.exports = init;
@@ -1,31 +1,29 @@
1
- const { createTask, resolve } = require('../../../utils/widgets');
2
- const { buildMpNpm } = require('../../../utils/mpCiUtils');
3
- const { CACHE_DIR, DEFAULT_MODULE_DIR } = require('../../../config/constant');
4
- const { cloudNpmInstall, mpNpmInstallAll } = require('../../../utils/npmUtils');
5
- const { tmsModulesMergeLocalModuleCfg } = require('../../../utils/tkitUtils');
6
-
7
- async function install(tmsConfig, modules, isCloud = true) {
8
- const newModules = tmsModulesMergeLocalModuleCfg(modules, tmsConfig.appName, DEFAULT_MODULE_DIR);
9
- // 小程序npm install
10
- await createTask(
11
- mpNpmInstallAll,
12
- '小程序 开始npm install',
13
- '小程序npm install 完成',
14
- )(newModules, resolve(tmsConfig.outputDir), `${CACHE_DIR}/node_modules`);
15
-
16
- // 构建miniprograme_npm
17
- await createTask(
18
- buildMpNpm,
19
- '开始构建miniprograme_npm',
20
- '构建miniprograme_npm 完成',
21
- )({ appId: tmsConfig.appId, projectPath: resolve('./'), privateKey: tmsConfig.privateKey });
22
-
23
- // 安装云函数的
24
- isCloud && createTask(
25
- cloudNpmInstall,
26
- '云函数npm install',
27
- '云函数npm install安装完毕',
28
- )(resolve(tmsConfig.cloudDir));
29
- }
30
-
31
- module.exports = install;
1
+ const { createTask, resolve } = require('../../../utils/widgets');
2
+ const { buildMpNpm } = require('../../../utils/mpCiUtils');
3
+ const { CACHE_DIR } = require('../../../config/constant');
4
+ const { cloudNpmInstall, mpNpmInstallAll } = require('../../../utils/npmUtils');
5
+
6
+ async function install(tmsConfig, modules, isCloud = false) {
7
+ // 小程序npm install
8
+ await createTask(
9
+ mpNpmInstallAll,
10
+ '小程序 开始npm install',
11
+ '小程序npm install 完成',
12
+ )(modules, resolve(tmsConfig.outputDir), `${CACHE_DIR}/node_modules`);
13
+
14
+ // 构建miniprograme_npm
15
+ await createTask(
16
+ buildMpNpm,
17
+ '开始构建miniprograme_npm',
18
+ '构建miniprograme_npm 完成',
19
+ )({ appId: tmsConfig.appId, projectPath: resolve('./'), privateKey: tmsConfig.privateKey });
20
+
21
+ // 安装云函数的
22
+ isCloud && createTask(
23
+ cloudNpmInstall,
24
+ '云函数npm install',
25
+ '云函数npm install安装完毕',
26
+ )(resolve(tmsConfig.cloudDir));
27
+ }
28
+
29
+ module.exports = install;