• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright (c) 2023 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 *     http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16import ts from 'typescript';
17import path from 'path';
18import fs from 'fs';
19import { createFilter } from '@rollup/pluginutils';
20import MagicString from 'magic-string';
21
22import {
23  LogInfo,
24  componentInfo,
25  emitLogInfo,
26  getTransformLog,
27  genTemporaryPath,
28  writeFileSync
29} from '../../utils';
30import {
31  preprocessExtend,
32  preprocessNewExtend,
33  validateUISyntax,
34  propertyCollection,
35  linkCollection,
36  resetComponentCollection,
37  componentCollection
38} from '../../validate_ui_syntax';
39import {
40  processUISyntax,
41  resetLog,
42  transformLog
43} from '../../process_ui_syntax';
44import {
45  abilityConfig,
46  projectConfig,
47  abilityPagesFullPath
48} from '../../../main';
49import { ESMODULE, JSBUNDLE } from '../../pre_define';
50import {
51  CUSTOM_BUILDER_METHOD,
52  GLOBAL_CUSTOM_BUILDER_METHOD,
53  INNER_CUSTOM_BUILDER_METHOD
54} from '../../component_map';
55
56const filter:any = createFilter(/(?<!\.d)\.(ets|ts)$/);
57const compilerOptions = ts.readConfigFile(
58  path.resolve(__dirname, '../../../tsconfig.json'), ts.sys.readFile).config.compilerOptions;
59compilerOptions['moduleResolution'] = 'nodenext';
60compilerOptions['module'] = 'es2020';
61
62let shouldDisableCache: boolean = false;
63const disableCacheOptions = {
64  bundleName: 'default',
65  entryModuleName: 'default',
66  runtimeOS: 'default',
67  resourceTableHash: 'default',
68  etsLoaderVersion: 'default'
69};
70
71export function etsTransform() {
72  const incrementalFileInHar: Map<string, string> = new Map();
73  return {
74    name: 'etsTransform',
75    transform: transform,
76    buildStart: judgeCacheShouldDisabled,
77    shouldInvalidCache(options) {
78      return shouldDisableCache;
79    },
80    moduleParsed(moduleInfo) {
81      if (projectConfig.compileHar) {
82        if (moduleInfo.id && !moduleInfo.id.match(/node_modules/) && !moduleInfo.id.startsWith('\x00')) {
83          const filePath: string = moduleInfo.id;
84          const jsCacheFilePath: string = genTemporaryPath(filePath, projectConfig.moduleRootPath,
85            process.env.cachePath, projectConfig);
86          const jsBuildFilePath: string = genTemporaryPath(filePath, projectConfig.moduleRootPath,
87            projectConfig.buildPath, projectConfig, true);
88          if (filePath.match(/\.e?ts$/)) {
89            incrementalFileInHar.set(jsCacheFilePath.replace(/\.ets$/, '.d.ets').replace(/\.ts$/, '.d.ts'),
90              jsBuildFilePath.replace(/\.ets$/, '.d.ets').replace(/\.ts$/, '.d.ts'));
91            incrementalFileInHar.set(jsCacheFilePath.replace(/\.e?ts$/, '.js'), jsBuildFilePath.replace(/\.e?ts$/, '.js'));
92          } else {
93            incrementalFileInHar.set(jsCacheFilePath, jsBuildFilePath);
94          }
95        }
96      }
97    },
98    afterBuildEnd() {
99      if (projectConfig.compileHar) {
100        incrementalFileInHar.forEach((jsBuildFilePath, jsCacheFilePath) => {
101          const sourceCode: string = fs.readFileSync(jsCacheFilePath, 'utf-8');
102          writeFileSync(jsBuildFilePath, sourceCode);
103        });
104      }
105      shouldDisableCache = false;
106      this.cache.set('disableCacheOptions', disableCacheOptions);
107    }
108  };
109}
110
111function judgeCacheShouldDisabled() {
112  for (const key in disableCacheOptions) {
113    if (!shouldDisableCache && this.cache.get('disableCacheOptions') && this.share &&
114      this.share.projectConfig && this.share.projectConfig[key] &&
115      this.cache.get('disableCacheOptions')[key] !== this.share.projectConfig[key]) {
116      shouldDisableCache = true;
117    }
118    if (this.share && this.share.projectConfig && this.share.projectConfig[key]) {
119      disableCacheOptions[key] = this.share.projectConfig[key];
120    }
121  }
122}
123
124function transform(code: string, id: string) {
125  if (!filter(id)) {
126    return null;
127  }
128
129  if (projectConfig.compileMode === ESMODULE) {
130    compilerOptions['importsNotUsedAsValues'] = 'remove';
131  }
132
133  const logger = this.share.getLogger('etsTransform');
134
135  const magicString = new MagicString(code);
136  const newContent: string = preProcess(code, id, this.getModuleInfo(id).isEntry, logger);
137
138  const result: ts.TranspileOutput = ts.transpileModule(newContent, {
139    compilerOptions: compilerOptions,
140    fileName: id,
141    transformers: { before: [ processUISyntax(null) ] }
142  });
143
144  resetCollection();
145  if (transformLog && transformLog.errors.length) {
146    emitLogInfo(logger, getTransformLog(transformLog), true, id);
147    resetLog();
148  }
149
150  return {
151    code: result.outputText,
152    map: result.sourceMapText ? JSON.parse(result.sourceMapText) : magicString.generateMap()
153  };
154}
155
156function preProcess(code: string, id: string, isEntry: boolean, logger: any): string {
157  if (/\.ets$/.test(id)) {
158    clearCollection();
159    let content = preprocessExtend(code);
160    content = preprocessNewExtend(content);
161    const fileQuery: string = isEntry && !abilityPagesFullPath.includes(id) ? '?entry' : '';
162    const log: LogInfo[] = validateUISyntax(code, content, id, fileQuery);
163    if (log.length) {
164      emitLogInfo(logger, log, true, id);
165    }
166    return content;
167  }
168  return code;
169}
170
171function clearCollection(): void {
172  componentCollection.customComponents.clear();
173  CUSTOM_BUILDER_METHOD.clear();
174  GLOBAL_CUSTOM_BUILDER_METHOD.clear();
175  INNER_CUSTOM_BUILDER_METHOD.clear();
176}
177
178function resetCollection() {
179  componentInfo.id = 0;
180  propertyCollection.clear();
181  linkCollection.clear();
182  resetComponentCollection();
183}
184