@symbo.ls/cli 2.11.190 → 2.11.191

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 (2) hide show
  1. package/bin/convert.js +2 -553
  2. package/package.json +3 -5
package/bin/convert.js CHANGED
@@ -1,393 +1,9 @@
1
1
  'use strict'
2
2
 
3
- import fs from 'fs'
4
- import chalk from 'chalk'
5
- import path from 'path'
6
3
  import { program } from './program.js'
7
- import { convert } from 'kalduna'
8
- import { parse } from 'globusa'
4
+ import { CLIconvert } from '@symbo.ls/convert/index.js'
9
5
 
10
- import * as esbuild from 'esbuild'
11
-
12
- // Set up jsdom
13
- import { JSDOM } from 'jsdom'
14
- const jsdom = new JSDOM('<html><head></head><body></body></html>')
15
- global.window = jsdom.window
16
- global.document = window.document
17
-
18
- const INTERNAL_UIKIT_CONF = {
19
- excludedComponents: [
20
- // We have our own implementations of these components
21
- 'Svg',
22
- 'Box',
23
- 'Icon',
24
- 'IconText',
25
- 'Tooltip',
26
-
27
- // These are not domql objects
28
- 'keySetters',
29
- 'getSystemTheme',
30
- 'splitTransition',
31
- 'transformDuration',
32
- 'transformShadow',
33
- 'transformTransition',
34
-
35
- // FIXME: Temporary list of components we want to skip
36
- 'DatePicker',
37
- 'DatePickerDay',
38
- 'DatePickerTwoColumns',
39
- 'DatePickerGrid',
40
- 'DatePickerGridContainer',
41
-
42
- // Not a domql object (headless-datepicker)
43
- 'calendar'
44
- ],
45
-
46
- // Can be strings or regex patterns
47
- excludedDirectories: [
48
- // TODO: Review these ignores with @nikoloza
49
- /Threejs$/,
50
- /Editorjs$/,
51
- /User$/
52
- ]
53
- }
54
6
  const TMP_DIR_NAME = '.smbls_convert_tmp'
55
- const TMP_DIR_PACKAGE_JSON_STR = JSON.stringify({
56
- name: 'smbls_convert_tmp',
57
- version: '1.0.0',
58
- // "main": "index.js",
59
- license: 'ISC'
60
- })
61
-
62
- function generatePackageJsonFile (
63
- sourcePackageJsonPath,
64
- destPath,
65
- globusaStruct,
66
- desiredFormat,
67
- options
68
- ) {
69
- // Extract package name from source package.json
70
- const str = fs.readFileSync(sourcePackageJsonPath, { encoding: 'utf8' })
71
- let packageStruct
72
- try {
73
- packageStruct = JSON.parse(str)
74
- } catch (error) {
75
- console.error(`Error when parsing ${sourcePackageJsonPath}`)
76
- return
77
- }
78
- const split = packageStruct.name.split('/')
79
- const packageName = split[split.length - 1]
80
-
81
- // Generate list of dependencies
82
- const deps = {
83
- 'css-in-props': 'latest',
84
- [`@emotion/${desiredFormat}`]: '^11.11.0',
85
- '@emotion/css': '^11.11.0',
86
- '@symbo.ls/create': 'latest',
87
- '@symbo.ls/react': 'latest'
88
- }
89
- globusaStruct.imports
90
- .filter(imp => imp.path.match(/^@symbo\.ls\//))
91
- .filter(imp => imp.path !== packageName)
92
- .forEach(imp => { deps[imp.path] = 'latest' })
93
-
94
- // Generate final package.json string
95
- const genStr = JSON.stringify({
96
- name: `@symbo.ls/${desiredFormat}-${packageName}`,
97
- version: packageStruct.version ?? '1.0.0',
98
- license: packageStruct.license ?? 'UNLICENSED',
99
- dependencies: deps,
100
- peerDependencies: {
101
- react: '^18.2.0',
102
- 'react-dom': '^18.2.0'
103
- },
104
- main: 'index.js',
105
- source: 'index.js'
106
- }, undefined, 2)
107
-
108
- fs.writeFileSync(destPath, genStr)
109
- }
110
-
111
- function isDirectory (dir) {
112
- const stat = fs.statSync(dir, { throwIfNoEntry: false })
113
- if (!stat) return false
114
-
115
- return stat.isDirectory()
116
- }
117
-
118
- // Essentially does 'mkdir -P'
119
- function mkdirp (dir) {
120
- try {
121
- return fs.mkdirSync(dir, { recursive: true })
122
- } catch (err) {
123
- if (err.code !== 'EEXIST') {
124
- throw err
125
- }
126
- }
127
- return null
128
- }
129
-
130
- // Returns a string
131
- export function convertDomqlModule (domqlModule, globusaStruct, desiredFormat, options = {}) {
132
- let convertedStr = ''
133
- const whitelist = (options.only ? options.only.split(',') : null)
134
-
135
- console.group()
136
- const exports = Object.keys(domqlModule)
137
- .filter(exportName => {
138
- if (!whitelist) return true
139
- if (!whitelist.includes(exportName)) {
140
- console.log(`Skipping ${exportName} component due to whitelist exclusion`)
141
- return false
142
- }
143
- return true
144
- })
145
- .filter(exportName => {
146
- if (!options.internalUikit) return true
147
- if (INTERNAL_UIKIT_CONF.excludedComponents.includes(exportName)) {
148
- console.log(`Skipping ${exportName} component due to internal uikit exclusion`)
149
- return false
150
- }
151
- return true
152
- })
153
-
154
- const uniqueImports = []
155
- let globalSymbolTable = {}
156
- for (const idx in exports) {
157
- const exportName = exports[idx]
158
-
159
- const dobj = domqlModule[exportName]
160
-
161
- // Set component name (if not present)
162
- if (!dobj.__name) {
163
- dobj.__name = exportName
164
- }
165
-
166
- console.group()
167
- console.log(dobj.__name) // NOTE: Don't remove this
168
-
169
- // NOTE: Don't use '===' here!
170
- const isFirst = (idx == 0) // eslint-disable-line
171
- const isLast = (idx == (exports.length - 1)) // eslint-disable-line
172
-
173
- const kaldunaOpts = {
174
- verbose: false,
175
- returnMitosisIR: true,
176
- globalSymbolTable,
177
- exportDefault: false,
178
- importsToRemove: uniqueImports,
179
-
180
- /* NOTE: The option below prevents a name collision bug. For example:
181
- export const A = { ... }
182
- export const B = { extends: A }
183
-
184
- Normally, converting component B will generate an import for A like:
185
- 'import { A } from @symbo.ls/react'
186
- But, in this case, because A is in local scope as one of the exports,
187
- the component import will be ignored, preventing the collision.
188
- */
189
- componentImportsToIgnore: exports
190
- }
191
-
192
- let out = null
193
- if (isFirst) {
194
- out = convert(dobj, desiredFormat, {
195
- ...kaldunaOpts,
196
- removeReactImport: false
197
- // NOTE(nikaoto): Commented these out because we're using deps now, so
198
- // all the imports and decls are going to be redundant
199
- // importsToInclude: globusaStruct.imports,
200
- // declarationsToInclude: globusaStruct.declarations,
201
- })
202
- } else {
203
- out = convert(dobj, desiredFormat, {
204
- ...kaldunaOpts,
205
- removeReactImport: true
206
- })
207
- }
208
-
209
- convertedStr = convertedStr + out.str
210
- if (!isLast) {
211
- convertedStr += '\n'
212
- }
213
- uniqueImports.push(...out.mitosisIR.imports)
214
- globalSymbolTable = out.mitosisIR._globalSymbolTable
215
- console.groupEnd()
216
- }
217
- console.groupEnd()
218
-
219
- return convertedStr
220
- }
221
-
222
- // Takes a source file, then bundles, parses and converts it and writes the
223
- // result to the destination. The tmpDirPath is used as a working directory for
224
- // temporary files.
225
- // Returns globusaStruct for later usage.
226
- async function convertFile (srcPath, tmpDirPath, destPath,
227
- desiredFormat, options) {
228
- // Parse with globusa
229
- console.log(`Parsing components in ${srcPath}`)
230
- const fileContent = await fs.promises.readFile(srcPath, 'utf8')
231
- const globusaStruct = parse(fileContent)
232
-
233
- const fileName = path.basename(srcPath)
234
- const bundledFilePath = path.resolve(tmpDirPath, fileName)
235
-
236
- // Bundle with esbuild
237
- await esbuild.build({
238
- entryPoints: [srcPath],
239
- bundle: true,
240
- sourcemap: true,
241
- keepNames: false,
242
- target: 'node12',
243
- format: 'cjs',
244
- outdir: tmpDirPath
245
- })
246
-
247
- // Import the bundled module to obtain exported domql objects
248
- console.log(`Importing ${bundledFilePath}`)
249
- const mod = (await import(bundledFilePath))
250
- const domqlModule = mod.default
251
-
252
- // Convert it/them with kalduna
253
- console.log(`Converting components in ${bundledFilePath}:`)
254
- const convertedModuleStr = convertDomqlModule(
255
- domqlModule,
256
- globusaStruct,
257
- desiredFormat,
258
- options
259
- )
260
-
261
- // Create dest dir
262
- mkdirp(path.dirname(destPath))
263
-
264
- // Write file
265
- if (convertedModuleStr && convertedModuleStr.length > 0) {
266
- const fh = await fs.promises.open(destPath, 'w')
267
- await fh.writeFile(convertedModuleStr, 'utf8')
268
- await fh.close()
269
- }
270
-
271
- return globusaStruct
272
- }
273
-
274
- // Aborts copying if destination exists
275
- function recursiveCopy (src, dst, exclude) {
276
- if (exclude && exclude.includes(src)) { return }
277
-
278
- if (!fs.existsSync(src)) {
279
- console.error(`Error (recursiveCopy): Source file '${src}' does not exist.`)
280
- return
281
- }
282
-
283
- if (fs.existsSync(dst)) {
284
- console.error(`Error (recursiveCopy): Destination file '${dst}' exists.`)
285
- return
286
- }
287
-
288
- if (!isDirectory(src)) {
289
- fs.copyFileSync(src, dst)
290
- return
291
- }
292
-
293
- mkdirp(dst)
294
- const files = fs.readdirSync(src)
295
- for (const f of files) {
296
- if (exclude && exclude.includes(f)) { continue }
297
-
298
- recursiveCopy(path.join(src, f), path.join(dst, f), exclude)
299
- }
300
- }
301
-
302
- function mergeDirectories (mrg, dst, { globusaMerge, exclude }) {
303
- // Source doesn't exist, skip
304
- if (!fs.existsSync(mrg)) {
305
- console.error(`Error: Source merge directory '${mrg}' does not exist.`)
306
- return
307
- }
308
-
309
- // Direct copy, no merging needed
310
- if (!fs.existsSync(dst)) {
311
- recursiveCopy(mrg, dst, exclude)
312
- return
313
- }
314
-
315
- const isMrgDir = isDirectory(mrg)
316
- const isDstDir = isDirectory(dst)
317
-
318
- if (!isMrgDir && !isDstDir) {
319
- return
320
- }
321
-
322
- if (!isMrgDir && isDstDir) {
323
- console.error(`mergeDirectories('${mrg}', '${dst}') skipped. ` +
324
- 'Merge source (mrg) is a regular file and the ' +
325
- 'destination (dst) is a directory.')
326
- return
327
- }
328
-
329
- if (isMrgDir && !isDstDir) {
330
- console.error(`mergeDirectories('${mrg}', '${dst}') skipped. ` +
331
- 'Merge source (mrg) is a directory and the ' +
332
- 'destination (dst) is a regular file.')
333
- return
334
- }
335
-
336
- const mrgFiles = fs.readdirSync(mrg).filter(f => !exclude.includes(f))
337
- const dstFiles = fs.readdirSync(dst)
338
-
339
- // Make a map of dstFiles for quick access
340
- const dstFilesMap = {}
341
- for (const f of dstFiles) {
342
- dstFilesMap[f] = true
343
- }
344
-
345
- // Do a direct directory merge (without globusa)
346
- const directMrgFiles = mrgFiles.filter(f => !globusaMerge.includes(f))
347
- for (const f of directMrgFiles) {
348
- if (!dstFilesMap[f]) {
349
- recursiveCopy(path.resolve(mrg, f), path.resolve(dst, f), exclude)
350
- } else {
351
- mergeDirectories(path.resolve(mrg, f), path.resolve(dst, f), {
352
- globusaMerge, exclude
353
- })
354
- }
355
- }
356
-
357
- // Do a smart file merge (with globusa)
358
- const globusaMrgFiles = mrgFiles.filter(f => globusaMerge.includes(f))
359
- for (const f of globusaMrgFiles) {
360
- if (!dstFilesMap[f]) {
361
- // Nothing to merge. Do a direct copy
362
- const p = path.resolve(mrg, f)
363
- if (isDirectory(p)) {
364
- console.error('Error: Globusa merge can only be done on files, ' +
365
- `but '${p}' is a directory`)
366
- } else {
367
- fs.copyFileSync(p, path.resolve(dst, f))
368
- }
369
- } else {
370
- // Concatenate the files
371
- const mrgTxt = fs.readFileSync(path.resolve(mrg, f), { encoding: 'utf8' })
372
- const dstTxt = fs.readFileSync(path.resolve(dst, f), { encoding: 'utf8' })
373
- const outTxt = mrgTxt + '\n' + dstTxt
374
-
375
- // TODO: Dedup the imports with globusa here
376
-
377
- fs.writeFileSync(path.resolve(dst, f), outTxt, { encoding: 'utf8' })
378
- }
379
- }
380
- }
381
-
382
- export function convertFromCli (data, opts) {
383
- const { framework, verbose, verboseCode } = opts
384
- console.log(chalk.dim('\n----------------\n'))
385
- console.log('Converting components to', chalk.bold(framework))
386
- const convertedStrings = convertDomqlModule(data, null, framework)
387
- if (verboseCode) console.log(convertedStrings)
388
- console.log(chalk.bold.green('\nSuccessfully converted'))
389
- return verbose
390
- }
391
7
 
392
8
  program
393
9
  .command('convert')
@@ -411,171 +27,4 @@ program
411
27
  .option('--internal-uikit',
412
28
  '(For internal use only). ' +
413
29
  'Excludes particular components from the conversion')
414
- .action(async (src, dest, options) => {
415
- if (!convert) {
416
- throw new Error(
417
- 'convert() from `kalduna` is not defined. Try to install ' +
418
- '`kalduna` and run this command again.')
419
- }
420
-
421
- if (!parse) {
422
- throw new Error(
423
- 'parse() from `globusa` is not defined. Try to install ' +
424
- '`globusa` and run this command again.')
425
- }
426
-
427
- // Desired format
428
- let desiredFormat = 'react'
429
- if (options.angular) {
430
- desiredFormat = 'angular'
431
- } else if (options.vue2) {
432
- desiredFormat = 'vue2'
433
- } else if (options.vue3) {
434
- desiredFormat = 'vue3'
435
- }
436
-
437
- // Resolve source file/dir
438
- const srcPath = path.resolve(src || './src')
439
- if (!fs.existsSync(srcPath)) {
440
- console.error(`Source directory/file ('${srcPath}') does not exist`)
441
- process.exit(1)
442
- }
443
-
444
- // Resolve & create tmp dir
445
- const tmpDirPath = options.tmpDir ??
446
- path.resolve(path.dirname(srcPath), TMP_DIR_NAME)
447
- mkdirp(tmpDirPath)
448
-
449
- // Put a package.json file so that when we import() the modules from the
450
- // directory, node doesn't recognize them as ES modules (in case the parent
451
- // directory of the tmp dir has "type": "module" in its package.json
452
- const pj = await fs.promises.open(
453
- path.resolve(tmpDirPath, 'package.json'), 'w')
454
- await pj.writeFile(TMP_DIR_PACKAGE_JSON_STR, 'utf8')
455
- await pj.close()
456
-
457
- // Convert single file. Output will also be a single file.
458
- if (!isDirectory(srcPath)) {
459
- // Determine destFilePath and create it if needed
460
- let destFilePath
461
- if (dest) {
462
- // dest is given.
463
- if (!fs.existsSync(dest)) {
464
- // dest doesn't exist. That's the output file we'll create.
465
- destFilePath = path.resolve(dest)
466
- } else if (isDirectory(dest)) {
467
- // dest exists and is a directory. Create our output file inside it.
468
- destFilePath = path.join(path.resolve(dest), path.basename(srcPath))
469
- } else {
470
- // dest exists and is not a directory. Overwrite the file.
471
- destFilePath = path.resolve(dest)
472
- }
473
- } else {
474
- // dest not given. Use default (desiredFormat as directory).
475
- const destDir = path.resolve(desiredFormat)
476
- mkdirp(destDir)
477
- destFilePath = path.join(destDir, path.basename(srcPath))
478
- }
479
-
480
- await convertFile(
481
- srcPath,
482
- tmpDirPath,
483
- destFilePath,
484
- desiredFormat,
485
- options
486
- )
487
-
488
- process.exit(0)
489
- }
490
-
491
- // We're converting multiple files (in a directory).
492
- // Determine destDirPath & create it if needed
493
- if (!dest) dest = path.resolve(desiredFormat)
494
- let destDirPath
495
- if (!fs.existsSync(dest)) {
496
- // dest doesn't exist. Create it.
497
- destDirPath = path.resolve(dest)
498
- mkdirp(destDirPath)
499
- } else if (isDirectory(dest)) {
500
- // dest exists and is a directory.
501
- destDirPath = path.resolve(dest)
502
- } else {
503
- // dest exists and is not a directory.
504
- console.error(
505
- `The destination ('${path.resolve(dest)}') must be a directory when ` +
506
- `the source ('${srcPath}') is a directory`)
507
- process.exit(1)
508
- }
509
-
510
- // Resolve merge dir
511
- let mergeDirPath = null
512
- if (options.merge && options.internalUikit) {
513
- mergeDirPath = path.resolve(options.merge)
514
- if (!fs.existsSync(mergeDirPath)) {
515
- console.error(`Merge directory '${mergeDirPath}' does not exist`)
516
- process.exit(1)
517
- }
518
- }
519
-
520
- const dontConvert = ['index.js', 'package.json', 'node_modules', 'dist']
521
- const sourceDirNames = (await fs.promises.readdir(srcPath))
522
- .filter(dir => !dontConvert.includes(dir))
523
-
524
- const dirs = []
525
-
526
- // Core convert loop
527
- for (const dir of sourceDirNames) {
528
- // Ignored directories
529
- if (options.internalUikit) {
530
- let skip = false
531
- for (const pat of INTERNAL_UIKIT_CONF.excludedDirectories) { if (dir.match(pat)) { skip = true; break } }
532
- if (skip) continue
533
- }
534
-
535
- const dirPath = path.join(srcPath, dir)
536
- if (!isDirectory(dirPath)) {
537
- console.log(`Skipping ${dirPath} because it is not a directory`)
538
- continue
539
- }
540
- const indexFilePath = path.join(dirPath, 'index.js')
541
- const pjFilePath = path.join(dirPath, 'package.json')
542
-
543
- const globusaStruct = await convertFile(
544
- indexFilePath, // src
545
- path.join(tmpDirPath, dir), // tmp
546
- path.join(destDirPath, dir, 'index.js'), // dst
547
- desiredFormat,
548
- options
549
- )
550
-
551
- if (options.internalUikit && fs.existsSync(pjFilePath)) {
552
- generatePackageJsonFile(
553
- pjFilePath, // src
554
- path.join(destDirPath, dir, 'package.json'), // dst
555
- globusaStruct,
556
- desiredFormat,
557
- options
558
- )
559
- }
560
-
561
- dirs.push(dir)
562
- }
563
-
564
- // Generate top index.js file
565
- if (dirs.length > 0) {
566
- const fileContent = dirs.map(d => `export * from './${d}'`).join('\n')
567
- const fh = await fs.promises.open(path.join(destDirPath, 'index.js'), 'w')
568
- await fh.writeFile(fileContent, 'utf8')
569
- await fh.close()
570
- }
571
-
572
- if (mergeDirPath) {
573
- console.log(`Merging '${mergeDirPath}' and ${destDirPath}...`)
574
- mergeDirectories(mergeDirPath, destDirPath, {
575
- globusaMerge: ['index.js', 'index.jsx'],
576
- exclude: ['dist', 'node_modules']
577
- })
578
- }
579
-
580
- process.exit(0)
581
- })
30
+ .action(CLIconvert)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@symbo.ls/cli",
3
- "version": "2.11.190",
3
+ "version": "2.11.191",
4
4
  "description": "Fetch your Symbols configuration",
5
5
  "main": "bin/fetch.js",
6
6
  "author": "Symbols",
@@ -21,13 +21,11 @@
21
21
  "chalk": "^5.0.0",
22
22
  "commander": "latest",
23
23
  "esbuild": "^0.19.2",
24
- "globusa": "latest",
25
- "jsdom": "^22.1.0",
26
24
  "node-fetch": "^3.1.0",
27
25
  "v8-compile-cache": "^2.3.0"
28
26
  },
29
27
  "peerDependencies": {
30
- "kalduna": "latest"
28
+ "@symbo.ls/convert": "latest"
31
29
  },
32
- "gitHead": "cd5a83d3c776262d6412caa196d3f010a3305b04"
30
+ "gitHead": "a78598a97a6531fa3689833f1525ba0a49a3f81f"
33
31
  }