agora-toolchain 3.8.1 → 3.9.0-alpha

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agora-toolchain",
3
- "version": "3.8.1",
3
+ "version": "3.9.0-alpha",
4
4
  "license": "MIT",
5
5
  "bin": {
6
6
  "agora-tc-transpile": "./scripts/transpile.js",
@@ -0,0 +1,26 @@
1
+ {
2
+ "presets": [
3
+ [
4
+ "@babel/preset-env",
5
+ {
6
+ "modules": false,
7
+ "useBuiltIns": "usage",
8
+ "corejs": {
9
+ "version": "3.33",
10
+ "proposals": true
11
+ }
12
+ }
13
+ ],
14
+ [
15
+ "@babel/preset-react",
16
+ {
17
+ "runtime": "automatic"
18
+ }
19
+ ],
20
+ "@babel/preset-typescript"
21
+ ],
22
+ "plugins": [
23
+ ["@babel/plugin-proposal-decorators", { "version": "2023-05" }],
24
+ ["@babel/plugin-transform-runtime", { "regenerator": true }]
25
+ ]
26
+ }
@@ -1,7 +1,8 @@
1
1
  import { defineConfig } from 'electron-vite';
2
2
  import commonjs from '@rollup/plugin-commonjs';
3
3
  import legacy from '@vitejs/plugin-legacy';
4
- import { existsSync } from 'fs';
4
+ import { existsSync, writeFileSync, unlinkSync } from 'fs';
5
+ import path from 'path';
5
6
  import { resolveCwd, resolveModule } from '../tools/paths';
6
7
 
7
8
  /**
@@ -15,6 +16,7 @@ function generateWorkspaceAlias(libNames) {
15
16
  try {
16
17
  const modulePath = resolveModule(libName);
17
18
  const srcPath = `${modulePath}/src`;
19
+ const libEsPath = `${modulePath}/lib-es`;
18
20
  // const libPath = `${modulePath}/lib`;
19
21
 
20
22
  // Check if src directory exists
@@ -23,19 +25,50 @@ function generateWorkspaceAlias(libNames) {
23
25
  // Link to src if it exists
24
26
  aliases.push(
25
27
  { find: new RegExp(`^${libName}/lib/(.*)`), replacement: `${srcPath}/$1` },
26
- { find: libName, replacement: srcPath },
28
+ { find: new RegExp(`^${libName}$`), replacement: srcPath },
27
29
  );
30
+ } else if (existsSync(libEsPath)) {
31
+ console.log(`source linker: ${libName} linked to lib-es`);
32
+ aliases.push({ find: new RegExp(`^${libName}/lib/(.*)`), replacement: `${libEsPath}/$1` });
33
+ if (existsSync(`${libEsPath}/index.js`)) {
34
+ aliases.push({ find: new RegExp(`^${libName}$`), replacement: libEsPath });
35
+ }
28
36
  }
37
+ // else if (existsSync(libPath)) {
38
+ // console.log(`source linker: ${libName} linked to lib`);
39
+ // aliases.push({ find: new RegExp(`^${libName}/lib/(.*)`), replacement: `${libPath}/$1` });
40
+ // if (existsSync(`${libPath}/index.js`)) {
41
+ // aliases.push({ find: new RegExp(`^${libName}$`), replacement: libPath });
42
+ // }
43
+ // }
29
44
  } catch (e) {
30
45
  console.log(`source linker: ${libName} not found, using default paths`);
31
46
  // Fallback to relative path if module not found
32
- const relativePath = resolveCwd(`../${libName}/src`);
33
- if (existsSync(relativePath)) {
34
- aliases.push(
35
- { find: new RegExp(`^${libName}/lib/(.*)`), replacement: `${relativePath}/$1` },
36
- { find: libName, replacement: relativePath },
37
- );
38
- }
47
+ // const relativePath = resolveCwd(`../${libName}/src`);
48
+ // const relativeLibEsPath = resolveCwd(`../${libName}/lib-es`);
49
+ // const relativeLibPath = resolveCwd(`../${libName}/lib`);
50
+ // if (existsSync(relativePath)) {
51
+ // aliases.push(
52
+ // { find: new RegExp(`^${libName}/lib/(.*)`), replacement: `${relativePath}/$1` },
53
+ // { find: new RegExp(`^${libName}$`), replacement: relativePath },
54
+ // );
55
+ // } else if (existsSync(relativeLibEsPath)) {
56
+ // aliases.push({
57
+ // find: new RegExp(`^${libName}/lib/(.*)`),
58
+ // replacement: `${relativeLibEsPath}/$1`,
59
+ // });
60
+ // if (existsSync(`${relativeLibEsPath}/index.js`)) {
61
+ // aliases.push({ find: new RegExp(`^${libName}$`), replacement: relativeLibEsPath });
62
+ // }
63
+ // } else if (existsSync(relativeLibPath)) {
64
+ // aliases.push({
65
+ // find: new RegExp(`^${libName}/lib/(.*)`),
66
+ // replacement: `${relativeLibPath}/$1`,
67
+ // });
68
+ // if (existsSync(`${relativeLibPath}/index.js`)) {
69
+ // aliases.push({ find: new RegExp(`^${libName}$`), replacement: relativeLibPath });
70
+ // }
71
+ // }
39
72
  }
40
73
  });
41
74
 
@@ -50,6 +83,88 @@ export default defineConfig(() => {
50
83
  const preloadEntry = process.env.VITE_PRELOAD_ENTRY || 'electron/preload/index.js';
51
84
  const rendererEntry =
52
85
  process.env.VITE_RENDERER_ENTRY || 'electron/renderer/public/index.vite.html';
86
+ const rendererFragments = process.env.fcr_fragments || '';
87
+
88
+ const generatedFragmentFiles = [];
89
+
90
+ const ensureFragmentHtml = (fragmentName) => {
91
+ const filePath = resolveCwd(`${fragmentName}.html`);
92
+
93
+ if (existsSync(filePath)) {
94
+ return filePath;
95
+ }
96
+
97
+ const html = `<!doctype html>
98
+ <html lang="en">
99
+ <head>
100
+ <meta charset="UTF-8" />
101
+ <title></title>
102
+ </head>
103
+ <body>
104
+ <div id="root"></div>
105
+ </body>
106
+ <script type="module">
107
+ import { title, windowOptions, webPreferences } from '/src/fragments/${fragmentName}/index.tsx';
108
+ if (title) document.title = title;
109
+ if (window.runtime && window.runtime.setFragmentOptions) {
110
+ window.runtime.setFragmentOptions('${fragmentName}', windowOptions || {}, webPreferences || {});
111
+ }
112
+ </script>
113
+ </html>
114
+ `;
115
+ writeFileSync(filePath, html);
116
+ generatedFragmentFiles.push(filePath);
117
+
118
+ return filePath;
119
+ };
120
+
121
+ const parseRendererFragments = (value) => {
122
+ if (!value) return {};
123
+
124
+ return value
125
+ .split(',')
126
+ .map((item) => item.trim())
127
+ .filter(Boolean)
128
+ .reduce((acc, item) => {
129
+ if (
130
+ !item.includes('=') &&
131
+ !item.includes('/') &&
132
+ !item.includes('\\') &&
133
+ !item.includes('.')
134
+ ) {
135
+ const fragmentName = item;
136
+ const htmlPath = ensureFragmentHtml(fragmentName);
137
+ if (htmlPath) {
138
+ acc[fragmentName] = htmlPath;
139
+ }
140
+ return acc;
141
+ }
142
+
143
+ const [name, fragmentPath] = item.includes('=') ? item.split('=') : [null, item];
144
+ const trimmedPath = fragmentPath?.trim();
145
+ const trimmedName =
146
+ (name && name.trim()) || path.basename(trimmedPath).replace(/\.html$/i, '');
147
+
148
+ if (!trimmedName || !trimmedPath) {
149
+ console.warn(`Invalid fragment entry: "${item}", expected name=path`);
150
+ return acc;
151
+ }
152
+
153
+ if (trimmedName === 'index') {
154
+ console.warn(`Skip fragment entry "${item}" because "index" is reserved`);
155
+ return acc;
156
+ }
157
+
158
+ const resolvedPath = resolveCwd(trimmedPath);
159
+ if (!existsSync(resolvedPath)) {
160
+ console.warn(`Fragment entry not found: ${trimmedPath}`);
161
+ return acc;
162
+ }
163
+
164
+ acc[trimmedName] = resolvedPath;
165
+ return acc;
166
+ }, {});
167
+ };
53
168
 
54
169
  // Check if entry files exist
55
170
  const mainEntryPath = resolveCwd(mainEntry);
@@ -63,7 +178,12 @@ export default defineConfig(() => {
63
178
  console.log('Using electron-vite config with:');
64
179
  console.log(' Main entry:', mainEntry, hasMain ? '✓' : '✗');
65
180
  console.log(' Preload entry:', preloadEntry, hasPreload ? '✓' : '✗');
181
+ const fragmentEntries = parseRendererFragments(rendererFragments);
182
+ const fragmentKeys = Object.keys(fragmentEntries);
66
183
  console.log(' Renderer entry:', rendererEntry, hasRenderer ? '✓' : '✗');
184
+ if (fragmentKeys.length) {
185
+ console.log(' Renderer fragments:', fragmentKeys.join(', '));
186
+ }
67
187
  console.log(' Output dir prefix:', outDirPrefix);
68
188
 
69
189
  if (!hasMain && !hasPreload && !hasRenderer) {
@@ -73,6 +193,45 @@ export default defineConfig(() => {
73
193
  process.exit(1);
74
194
  }
75
195
 
196
+ const external = [
197
+ 'agora-electron-sdk',
198
+ 'electron',
199
+ 'winston',
200
+ 'winston-daily-rotate-file',
201
+ 'node:fs',
202
+ 'node:fs/promises',
203
+ 'fs',
204
+ 'node:path',
205
+ 'path',
206
+ 'node:os',
207
+ 'os',
208
+ 'electron-screenshots',
209
+ 'original-fs',
210
+ ];
211
+
212
+ const resolveWorkspaceLib = (libName) => {
213
+ try {
214
+ const modulePath = resolveModule(libName);
215
+ const srcPath = `${modulePath}/src`;
216
+ const libEsPath = `${modulePath}/lib-es`;
217
+ const libPath = `${modulePath}/lib`;
218
+ if (existsSync(srcPath)) {
219
+ return { basePath: srcPath, modulePath };
220
+ }
221
+ if (existsSync(libEsPath)) {
222
+ return { basePath: libEsPath, modulePath };
223
+ }
224
+ if (existsSync(libPath)) {
225
+ return { basePath: libPath, modulePath };
226
+ }
227
+ } catch (e) {
228
+ return null;
229
+ }
230
+ return null;
231
+ };
232
+
233
+ const foundationPaths = resolveWorkspaceLib('agora-foundation');
234
+
76
235
  if (hasMain) {
77
236
  config.main = {
78
237
  // 主进程构建配置
@@ -82,20 +241,20 @@ export default defineConfig(() => {
82
241
  input: {
83
242
  index: resolveCwd(mainEntry),
84
243
  },
85
- external: ['electron-screenshots', 'original-fs'],
244
+ external,
86
245
  plugins: [
87
- commonjs({
88
- include: [
89
- /fcr-ui-scene[/\\]lib[/\\].*/,
90
- /fcr-ui-scene-mobile[/\\]lib[/\\].*/,
91
- /fcr-ui-widget-sdk[/\\]lib[/\\].*/,
92
- /agora-ui-foundation[/\\]lib[/\\].*/,
93
- /fcr-core[/\\]lib[/\\].*/,
94
- /agora-rte-sdk[/\\]lib[/\\].*/,
95
- /agora-foundation[/\\]lib[/\\].*/,
96
- /foundation[/\\]lib[/\\].*/,
97
- ],
98
- }),
246
+ // commonjs({
247
+ // include: [
248
+ // /fcr-ui-scene[/\\]lib[/\\].*/,
249
+ // /fcr-ui-scene-mobile[/\\]lib[/\\].*/,
250
+ // /fcr-ui-widget-sdk[/\\]lib[/\\].*/,
251
+ // /agora-ui-foundation[/\\]lib[/\\].*/,
252
+ // /fcr-core[/\\]lib[/\\].*/,
253
+ // /agora-rte-sdk[/\\]lib[/\\].*/,
254
+ // /agora-foundation[/\\]lib[/\\].*/,
255
+ // /foundation[/\\]lib[/\\].*/,
256
+ // ],
257
+ // }),
99
258
  ],
100
259
  },
101
260
  bytecode: true,
@@ -115,19 +274,20 @@ export default defineConfig(() => {
115
274
  input: {
116
275
  index: resolveCwd(preloadEntry),
117
276
  },
277
+ external,
118
278
  plugins: [
119
- commonjs({
120
- include: [
121
- /fcr-ui-scene[/\\]lib[/\\].*/,
122
- /fcr-ui-scene-mobile[/\\]lib[/\\].*/,
123
- /fcr-ui-widget-sdk[/\\]lib[/\\].*/,
124
- /agora-ui-foundation[/\\]lib[/\\].*/,
125
- /fcr-core[/\\]lib[/\\].*/,
126
- /agora-rte-sdk[/\\]lib[/\\].*/,
127
- /agora-foundation[/\\]lib[/\\].*/,
128
- /foundation[/\\]lib[/\\].*/,
129
- ],
130
- }),
279
+ // commonjs({
280
+ // include: [
281
+ // /fcr-ui-scene[/\\]lib[/\\].*/,
282
+ // /fcr-ui-scene-mobile[/\\]lib[/\\].*/,
283
+ // /fcr-ui-widget-sdk[/\\]lib[/\\].*/,
284
+ // /agora-ui-foundation[/\\]lib[/\\].*/,
285
+ // /fcr-core[/\\]lib[/\\].*/,
286
+ // /agora-rte-sdk[/\\]lib[/\\].*/,
287
+ // /agora-foundation[/\\]lib[/\\].*/,
288
+ // /foundation[/\\]lib[/\\].*/,
289
+ // ],
290
+ // }),
131
291
  ],
132
292
  },
133
293
  bytecode: true,
@@ -140,19 +300,29 @@ export default defineConfig(() => {
140
300
 
141
301
  if (hasRenderer) {
142
302
  config.renderer = {
303
+ worker: {
304
+ rollupOptions: {
305
+ external,
306
+ },
307
+ plugins: () => [
308
+ // commonjs({
309
+ // include: [/foundation[/\\]lib/, /agora-foundation[/\\]lib/, /agora-rte-sdk[/\\]lib/],
310
+ // }),
311
+ ],
312
+ },
143
313
  plugins: [
144
- commonjs({
145
- include: [
146
- /fcr-ui-scene[/\\]lib[/\\].*/,
147
- /fcr-ui-scene-mobile[/\\]lib[/\\].*/,
148
- /fcr-ui-widget-sdk[/\\]lib[/\\].*/,
149
- /agora-ui-foundation[/\\]lib[/\\].*/,
150
- /fcr-core[/\\]lib[/\\].*/,
151
- /agora-rte-sdk[/\\]lib[/\\].*/,
152
- /agora-foundation[/\\]lib[/\\].*/,
153
- /foundation[/\\]lib[/\\].*/,
154
- ],
155
- }),
314
+ // commonjs({
315
+ // include: [
316
+ // /fcr-ui-scene[/\\]lib[/\\].*/,
317
+ // /fcr-ui-scene-mobile[/\\]lib[/\\].*/,
318
+ // /fcr-ui-widget-sdk[/\\]lib[/\\].*/,
319
+ // /agora-ui-foundation[/\\]lib[/\\].*/,
320
+ // /fcr-core[/\\]lib[/\\].*/,
321
+ // /agora-rte-sdk[/\\]lib[/\\].*/,
322
+ // /agora-foundation[/\\]lib[/\\].*/,
323
+ // /foundation[/\\]lib[/\\].*/,
324
+ // ],
325
+ // }),
156
326
  legacy({
157
327
  targets: ['defaults'],
158
328
  modernPolyfills: true,
@@ -160,6 +330,17 @@ export default defineConfig(() => {
160
330
  // MapIterator.find
161
331
  additionalLegacyPolyfills: ['core-js/proposals/iterator-helpers'],
162
332
  }),
333
+ {
334
+ name: 'fcr-clean-fragment-html',
335
+ closeBundle() {
336
+ if (!generatedFragmentFiles.length) return;
337
+ generatedFragmentFiles.forEach((filePath) => {
338
+ if (existsSync(filePath)) {
339
+ unlinkSync(filePath);
340
+ }
341
+ });
342
+ },
343
+ },
163
344
  ],
164
345
  // 渲染进程构建配置
165
346
  root: '.',
@@ -167,13 +348,23 @@ export default defineConfig(() => {
167
348
  build: {
168
349
  outDir: `${outDirPrefix}/electron/renderer`,
169
350
  rollupOptions: {
351
+ external,
170
352
  input: {
171
353
  index: resolveCwd(rendererEntry),
354
+ ...fragmentEntries,
172
355
  },
173
356
  },
174
357
  },
175
358
  resolve: {
176
359
  alias: [
360
+ ...(foundationPaths
361
+ ? [
362
+ {
363
+ find: /^agora-foundation\/lib\/(.*)/,
364
+ replacement: `${foundationPaths.basePath}/$1`,
365
+ },
366
+ ]
367
+ : []),
177
368
  // Node.js polyfills (对应 webpack 的 resolve.fallback)
178
369
  { find: 'crypto', replacement: 'crypto-browserify' },
179
370
  { find: 'stream', replacement: 'stream-browserify' },
@@ -56,6 +56,7 @@ module.exports = {
56
56
  'node:os': 'commonjs2 node:os',
57
57
  os: 'commonjs2 os',
58
58
  'winston-daily-rotate-file': 'commonjs2 winston-daily-rotate-file',
59
+ util: 'commonjs2 util',
59
60
  },
60
61
  module: {
61
62
  rules: [
@@ -2,15 +2,17 @@
2
2
  const { spawn } = require('../tools/process-utils');
3
3
  const fs = require('fs');
4
4
  const path = require('path');
5
- const { src, out, transpileOnly } = require('../tools/transpile-options');
5
+ const { src, out, transpileOnly, esm } = require('../tools/transpile-options');
6
6
  const presetsDir = path.join(__dirname, '../presets');
7
7
  const babelPresetPath = path.join(presetsDir, '.babelrc');
8
+ const babelEsmPresetPath = path.join(presetsDir, '.babelrc.esm');
8
9
  const tsPresetPath = path.join(presetsDir, 'tsconfig.json');
9
10
 
10
11
  const cwd = process.cwd();
11
12
 
12
13
  const srcDir = path.join(cwd, src ?? 'src');
13
14
  const outDir = path.join(cwd, out ?? 'lib');
15
+ const outEsmDir = path.join(cwd, 'lib-es');
14
16
 
15
17
  // console.log("Agora Toolchain: transpiling...");
16
18
  spawn(
@@ -54,6 +56,34 @@ spawn(
54
56
  }
55
57
  });
56
58
  }
59
+
60
+ if (esm) {
61
+ spawn(
62
+ require.resolve('.bin/babel'),
63
+ [
64
+ srcDir,
65
+ '--extensions',
66
+ '.ts,.tsx',
67
+ '--out-dir',
68
+ outEsmDir,
69
+ '--ignore',
70
+ '**/*.d.ts',
71
+ '--config-file',
72
+ babelEsmPresetPath,
73
+ '--copy-files',
74
+ ],
75
+ {
76
+ stdio: 'inherit',
77
+ },
78
+ ).on('exit', (esmCode) => {
79
+ if (esmCode !== 0) {
80
+ console.error('Agora Toolchain: ESM transpilation failed! code:', esmCode);
81
+ process.exit(1);
82
+ }
83
+
84
+ console.log('Agora Toolchain: ESM transpilation successful!');
85
+ });
86
+ }
57
87
  } else {
58
88
  console.error('Agora Toolchain: transpilation failed! code:', code);
59
89
  process.exit(1);
@@ -4,6 +4,7 @@ const opts = program
4
4
  .option('--transpile-only', 'Only transpile the code, do not build types')
5
5
  .option('--src <src>', 'Source directory')
6
6
  .option('--out <out>', 'Output directory')
7
+ .option('--esm', 'Also output ESM modules')
7
8
  .parse(process.argv)
8
9
  .opts();
9
10
 
package/env/tsconfig.json DELETED
@@ -1,4 +0,0 @@
1
- {
2
- "extends": "../presets/tsconfig.base.json",
3
- "files": ["./electron.setup.ts", "./karma-setup.ts"]
4
- }
@@ -1,14 +0,0 @@
1
- // excluding regex trick: http://www.rexegg.com/regex-best-trick.html
2
-
3
- // Not anything inside double quotes
4
- // Not anything inside single quotes
5
- // Not anything inside url()
6
- // Any digit followed by px
7
- // !singlequotes|!doublequotes|!url()|pixelunit
8
- function getUnitRegexp(unit) {
9
- return new RegExp('"[^"]+"|\'[^\']+\'|url\\([^\\)]+\\)|(\\d*\\.?\\d+)' + unit, 'g');
10
- }
11
-
12
- module.exports = {
13
- getUnitRegexp,
14
- };
@@ -1,118 +0,0 @@
1
- var filterPropList = {
2
- exact: function (list) {
3
- return list.filter(function (m) {
4
- return m.match(/^[^\*\!]+$/);
5
- });
6
- },
7
- contain: function (list) {
8
- return list
9
- .filter(function (m) {
10
- return m.match(/^\*.+\*$/);
11
- })
12
- .map(function (m) {
13
- return m.substr(1, m.length - 2);
14
- });
15
- },
16
- endWith: function (list) {
17
- return list
18
- .filter(function (m) {
19
- return m.match(/^\*[^\*]+$/);
20
- })
21
- .map(function (m) {
22
- return m.substr(1);
23
- });
24
- },
25
- startWith: function (list) {
26
- return list
27
- .filter(function (m) {
28
- return m.match(/^[^\*\!]+\*$/);
29
- })
30
- .map(function (m) {
31
- return m.substr(0, m.length - 1);
32
- });
33
- },
34
- notExact: function (list) {
35
- return list
36
- .filter(function (m) {
37
- return m.match(/^\![^\*].*$/);
38
- })
39
- .map(function (m) {
40
- return m.substr(1);
41
- });
42
- },
43
- notContain: function (list) {
44
- return list
45
- .filter(function (m) {
46
- return m.match(/^\!\*.+\*$/);
47
- })
48
- .map(function (m) {
49
- return m.substr(2, m.length - 3);
50
- });
51
- },
52
- notEndWith: function (list) {
53
- return list
54
- .filter(function (m) {
55
- return m.match(/^\!\*[^\*]+$/);
56
- })
57
- .map(function (m) {
58
- return m.substr(2);
59
- });
60
- },
61
- notStartWith: function (list) {
62
- return list
63
- .filter(function (m) {
64
- return m.match(/^\![^\*]+\*$/);
65
- })
66
- .map(function (m) {
67
- return m.substr(1, m.length - 2);
68
- });
69
- },
70
- };
71
-
72
- function createPropListMatcher(propList) {
73
- var hasWild = propList.indexOf('*') > -1;
74
- var matchAll = hasWild && propList.length === 1;
75
- var lists = {
76
- exact: filterPropList.exact(propList),
77
- contain: filterPropList.contain(propList),
78
- startWith: filterPropList.startWith(propList),
79
- endWith: filterPropList.endWith(propList),
80
- notExact: filterPropList.notExact(propList),
81
- notContain: filterPropList.notContain(propList),
82
- notStartWith: filterPropList.notStartWith(propList),
83
- notEndWith: filterPropList.notEndWith(propList),
84
- };
85
- return function (prop) {
86
- if (matchAll) return true;
87
- return (
88
- (hasWild ||
89
- lists.exact.indexOf(prop) > -1 ||
90
- lists.contain.some(function (m) {
91
- return prop.indexOf(m) > -1;
92
- }) ||
93
- lists.startWith.some(function (m) {
94
- return prop.indexOf(m) === 0;
95
- }) ||
96
- lists.endWith.some(function (m) {
97
- return prop.indexOf(m) === prop.length - m.length;
98
- })) &&
99
- !(
100
- lists.notExact.indexOf(prop) > -1 ||
101
- lists.notContain.some(function (m) {
102
- return prop.indexOf(m) > -1;
103
- }) ||
104
- lists.notStartWith.some(function (m) {
105
- return prop.indexOf(m) === 0;
106
- }) ||
107
- lists.notEndWith.some(function (m) {
108
- return prop.indexOf(m) === prop.length - m.length;
109
- })
110
- )
111
- );
112
- };
113
- }
114
-
115
- module.exports = {
116
- filterPropList,
117
- createPropListMatcher,
118
- };