squared 0.5.18 → 0.6.0

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.
@@ -5,37 +5,41 @@ module Squared
5
5
  module Project
6
6
  class Node < Git
7
7
  OPT_NPM = {
8
- common: %w[dry-run=!? include-workspace-root=!? loglevel=b workspaces=!? w|workspace=v].freeze,
9
- install: %w[package-lock-only=!? prefer-dedupe=!? E|save-exact=!? before=q cpu=b libc=b os=b].freeze,
10
- install_base: %w[audit=! bin-links=! foreground-scripts=!? fund=! ignore-scripts=!? install-links=!?
11
- package-lock=! strict-peer-deps=!? include=b install-strategy=b omit=b].freeze,
12
- install_no: %w[audit bin-links fund package-lock].freeze,
13
- install_as: %w[no-save B|save-bundle D|save-dev O|save-optional save-peer P|save-prod g|global=!?
14
- S|save=!?].freeze,
8
+ common: %w[dry-run=!? loglevel=b include-workspace-root=!? workspaces=!? w|workspace=v].freeze,
9
+ install: %w[package-lock-only=!? prefer-dedupe=!? cpu=b libc=b os=b].freeze,
10
+ install_a: %w[audit=! bin-links=! fund=! ignore-scripts=!? install-links=!? package-lock=! strict-peer-deps=!?
11
+ include=b install-strategy=b omit=b].freeze,
12
+ install_b: %w[no-save save-bundle save-dev save-optional save-peer save-prod before=q foreground-scripts=!?
13
+ g|global=!? S|save=!? E|save-exact=!?].freeze,
15
14
  run: %w[foreground-scripts=!? if-present=!? ignore-scripts=!? script-shell=p].freeze,
16
15
  exec: %w[c|call=q package=b].freeze,
17
- pack: %w[ignore-scripts=!? json=!? pack-destination=p].freeze
16
+ pack: %w[ignore-scripts=!? json=!? pack-destination=p].freeze,
17
+ no: {
18
+ install: %w[audit bin-links fund package-lock].freeze
19
+ }.freeze
18
20
  }.freeze
19
21
  OPT_PNPM = {
20
- common: %w[aggregate-output color ignore-workspace-root-check no-color stream use-stderr C|dir=p loglevel=b
21
- r|recursive w|workspace-root].freeze,
22
- cpu: %w[cpu=b libc=b os=b].freeze,
23
- filter: %w[fail-if-no-match changed-files-ignore-pattern=q filter=q filter-prod=q test-pattern=q].freeze,
22
+ common: %w[aggregate-output color no-color stream use-stderr C|dir=p loglevel=b w|workspace-root].freeze,
23
+ common_cpu: %w[cpu=b libc=b os=b].freeze,
24
+ common_filter: %w[fail-if-no-match changed-files-ignore-pattern=q filter=q filter-prod=q
25
+ test-pattern=q].freeze,
24
26
  install: %w[fix-lockfile force ignore-pnpmfile ignore-workspace lockfile-only merge-git-branch-lockfiles
25
27
  optimistic-repeat-install no-hoist no-lockfile no-optional prefer-frozen-lockfile resolution-only
26
28
  shamefully-hoist side-effects-cache side-effects-cache-readonly s|silent strict-peer-dependencies
27
29
  use-running-store-server use-store-server child-concurrency=i hoist-pattern=q lockfile-dir=p
28
30
  modules-dir=p network-concurrency=i package-import-method=b public-hoist-pattern=q
29
31
  reporter=b].freeze,
30
- install_base: %w[global-dir ignore-scripts offline prefer-offline store-dir=p virtual-store-dir=p].freeze,
31
- install_no: %w[frozen-lockfile verify-store-integrity].freeze,
32
- install_as: %w[D|dev no-optional P|prod].freeze,
32
+ install_a: %w[global-dir ignore-scripts offline prefer-offline store-dir=p virtual-store-dir=p].freeze,
33
+ install_b: %w[D|dev global-dir no-optional P|prod r|recursive].freeze,
33
34
  update: %w[g|global i|interactive L|latest depth=i].freeze,
34
35
  dedupe: %w[check].freeze,
35
- run: %w[if-present no-bail parallel report-summary reporter-hide-prefix resume-from
36
+ run: %w[if-present no-bail parallel r|recursive report-summary reporter-hide-prefix resume-from
36
37
  sequential].freeze,
37
- exec: %w[no-reporter-hide-prefix parallel report-summary resume-from c|shell-mode].freeze,
38
- pack: %w[json pack-destination=p pack-gzip-level=i].freeze
38
+ exec: %w[no-reporter-hide-prefix parallel r|recursive report-summary resume-from c|shell-mode].freeze,
39
+ pack: %w[json r|recursive pack-destination=p pack-gzip-level=i out=p workspace-concurrency=i].freeze,
40
+ no: {
41
+ install: %w[frozen-lockfile verify-store-integrity].freeze
42
+ }.freeze
39
43
  }.freeze
40
44
  OPT_YARN = {
41
45
  common: %w[check-files disable-pnp enable-pnp flat focus force frozen-lockfile json har ignore-engines
@@ -57,11 +61,45 @@ module Squared
57
61
  run: %w[B|binaries-only inspect inspect-brk T|top-level require=q].freeze,
58
62
  pack: %w[n|dry-run install-if-needed json o|out=p].freeze
59
63
  }.freeze
60
- private_constant :OPT_NPM, :OPT_PNPM, :OPT_YARN, :OPT_BERRY
64
+ OPT_TSC = {
65
+ base: %w[all b|build init listFilesOnly locale=b p|project=p showConfig w|watch].freeze,
66
+ compiler: %w[allowArbitraryExtensions=!? allowImportingTsExtensions=!? allowJs=!?
67
+ allowSyntheticDefaultImports=!? allowUmdGlobalAccess=!? allowUnreachableCode=!?
68
+ allowUnusedLabels=!? alwaysStrict=!? assumeChangesOnlyAffectDirectDependencies=!? baseUrl=p
69
+ charset=b checkJs=!? composite=!? customConditions=q d|declaration=!? declarationDir=p
70
+ declarationMap=!? diagnostics=!? disableReferencedProjectLoad=!? disableSizeLimit=!?
71
+ disableSolutionSearching=!? downlevelIteration=!? emitBOM=!? emitDeclarationOnly=!?
72
+ emitDecoratorMetadata=!? erasableSyntaxOnly=!? esModuleInterop=!? exactOptionalPropertyTypes=!?
73
+ experimentalDecorators=!? explainFiles=!? extendedDiagnostics=!?
74
+ forceConsistentCasingInFileNames=!? generateCpuProfile=b importHelpers=!?
75
+ importsNotUsedAsValues=b incremental=!? inlineSourceMap=!? inlineSources=!?
76
+ isolatedDeclarations=!? isolatedModules=!? jsx=b jsxFactory=q jsxFragmentFactory=q
77
+ jsxImportSource=q keyofStringsOnly=!? lib=q libReplacement=!? listEmittedFiles=!? listFiles=!?
78
+ mapRoot=p maxNodeModuleJsDepth=i m|module=b moduleDetection=b moduleResolution=b moduleSuffixes=b
79
+ newLine=b noCheck=!? noEmit=!? noEmitHelpers=!? noEmitOnError=!? noErrorTruncation=!?
80
+ noFallthroughCasesInSwitch=!? noImplicitAny=!? noImplicitOverride=!? noImplicitReturns=!?
81
+ noImplicitThis=!? noImplicitUseStrict=!? noLib=!? noPropertyAccessFromIndexSignature=!?
82
+ noResolve=!? noStrictGenericChecks=!? noUncheckedIndexedAccess=!? noUncheckedSideEffectImports=!?
83
+ noUnusedLocals=!? noUnusedParameters=!? outDir=p outFile=p paths=q plugins=b
84
+ preserveConstEnums=!? preserveSymlinks=!? preserveValueImports=!? preserveWatchOutput=!?
85
+ pretty=!? reactNamespace=b removeComments=!? resolveJsonModule=!? resolvePackageJsonExports=!?
86
+ resolvePackageJsonImports=!? rewriteRelativeImportExtensions=!? rootDir=p rootDirs=p
87
+ skipDefaultLibCheck=!? skipLibCheck=!? sourceMap=!? sourceRoot=p stopBuildOnErrors=!? strict=!?
88
+ strictBindCallApply=!? strictBuiltinIteratorReturn=!? strictFunctionTypes=!? strictNullChecks=!?
89
+ strictPropertyInitialization=!? stripInternal=!? suppressExcessPropertyErrors=!?
90
+ suppressImplicitAnyIndexErrors=!? t|target=b traceResolution=!? tsBuildInfoFile=p typeRoots=p
91
+ types=b useDefineForClassFields=!? useUnknownInCatchVariables=!? verbatimModuleSyntax=!?].freeze,
92
+ build: %w[clean=!? dry=!? force=!? v|verbose=!?].freeze,
93
+ watch: %w[excludeDirectories=p excludeFiles=p fallbackPolling=b synchronousWatchDirectory=!? watchDirectory=b
94
+ watchFile=b].freeze
95
+ }.freeze
96
+ PASS_NODE = {
97
+ tsc: %w[excludeDirectories excludeFiles customConditions lib moduleSuffixes plugins rootDirs typeRoots
98
+ types].freeze
99
+ }.freeze
100
+ private_constant :OPT_NPM, :OPT_PNPM, :OPT_YARN, :OPT_BERRY, :OPT_TSC, :PASS_NODE
61
101
 
62
102
  class << self
63
- def populate(*); end
64
-
65
103
  def tasks
66
104
  %i[outdated update publish].freeze
67
105
  end
@@ -90,10 +128,11 @@ module Squared
90
128
  end
91
129
 
92
130
  subtasks({
93
- 'package' => %i[install dedupe update].freeze,
131
+ 'package' => %i[install reinstall dedupe update].freeze,
94
132
  'outdated' => %i[major minor patch].freeze,
95
133
  'bump' => %i[version major minor patch].freeze,
96
134
  'publish' => %i[latest tag].freeze,
135
+ 'tsc' => %i[project build].freeze,
97
136
  'add' => nil,
98
137
  'run' => nil,
99
138
  'exec' => nil,
@@ -101,7 +140,7 @@ module Squared
101
140
  'pack' => nil
102
141
  })
103
142
 
104
- def initialize(*, init: nil, asdf: 'nodejs', **kwargs)
143
+ def initialize(*, init: nil, ts: 'tsconfig.json', asdf: 'nodejs', **kwargs)
105
144
  super
106
145
  if @pass.include?(Node.ref)
107
146
  initialize_ref Node.ref
@@ -110,7 +149,9 @@ module Squared
110
149
  initialize_build(Node.ref, prod: prod?, **kwargs)
111
150
  initialize_env(**kwargs)
112
151
  end
113
- @dependfile = basepath 'package.json'
152
+ @dependname = 'package.json'
153
+ dependfile_set [@dependname]
154
+ @tsfile = basepath(ts).yield_self { |file| file if file.exist? }
114
155
  @pm = { __: init }
115
156
  end
116
157
 
@@ -155,13 +196,12 @@ module Squared
155
196
  opts = []
156
197
  args.each do |val|
157
198
  if (n, extra = indexitem(val))
158
- if (item = list[n - 1])
199
+ if (item = list[n.pred])
159
200
  run compose([item.first, extra].compact.join(' '), script: true)
160
201
  elsif exception
161
202
  indexerror n, list
162
203
  else
163
- log.warn "run script #{n} of #{list.size} (out of range)"
164
- next
204
+ next log.warn "run script #{n} of #{list.size} (out of range)"
165
205
  end
166
206
  else
167
207
  opts << val
@@ -172,13 +212,13 @@ module Squared
172
212
  list = if (yarn = dependtype(:yarn)) > 0
173
213
  yarn == 1 ? OPT_YARN[:run] + OPT_YARN[:common] : OPT_BERRY[:run]
174
214
  elsif pnpm?
175
- OPT_PNPM[:run] + OPT_PNPM[:filter] + OPT_PNPM[:common]
215
+ pnpmopts :run, :common_filter
176
216
  else
177
- OPT_NPM[:run] + OPT_NPM[:common]
217
+ npmopts :run
178
218
  end
179
- op = OptionPartition.new(opts, list, session(dependbin, 'run'), project: self)
180
- op << op.extras.shift
181
- op.append(delim: true, quote: false)
219
+ OptionPartition.new(opts, list, session(dependbin, 'run'), project: self)
220
+ .add_first
221
+ .append(delim: true, quote: false)
182
222
  run(from: :run)
183
223
  end
184
224
  end
@@ -187,23 +227,23 @@ module Squared
187
227
  task action, [:package] do |_, args|
188
228
  if (package = args.package)
189
229
  args = args.extras
190
- if pnpm?
191
- pre = ->(ch) { "-#{ch}" if (ch = args.delete(ch)) }
192
- cmd = session 'pnpm', pre.call('r'), pre.call('c'), 'exec'
193
- list = OPT_PNPM[:exec] + OPT_PNPM[:filter] + OPT_PNPM[:common]
194
- else
195
- cmd = session 'npm', 'exec'
196
- list = OPT_NPM[:exec] + OPT_NPM[:common]
197
- end
230
+ cmd = if pnpm?
231
+ pre = ->(ch) { "-#{ch}" if args.delete(ch) }
232
+ list = pnpmopts :exec, :common_filter
233
+ session 'pnpm', pre.call('r'), pre.call('c'), 'exec'
234
+ else
235
+ list = npmopts :exec
236
+ session 'npm', 'exec'
237
+ end
198
238
  op = OptionPartition.new(args, list, cmd, project: self)
199
239
  if op.empty?
200
240
  op << package
201
241
  if (args = readline('Enter arguments', force: false))
202
- op.delim unless pnpm?
242
+ op << '--' unless pnpm?
203
243
  op << args
204
244
  end
205
245
  else
206
- op.delim unless pnpm?
246
+ op << '--' unless pnpm?
207
247
  op << package << op.join(' ')
208
248
  end
209
249
  else
@@ -233,16 +273,22 @@ module Squared
233
273
  flags.each do |flag|
234
274
  case action
235
275
  when 'outdated'
236
- format_desc(action, flag, %w[update interactive dry-run], arg: 'opts?')
276
+ format_desc action, flag, %w[u/pdate s/elect i/nteractive d/ry-run diff]
237
277
  task flag do |_, args|
238
278
  outdated flag, args.to_a
239
279
  end
240
280
  when 'package'
241
- format_desc(action, flag, 'opts*', after: flag == :dedupe ? nil : 'name*')
281
+ format_desc(action, flag, 'opts*', before: case flag
282
+ when :dedupe then nil
283
+ when :reinstall then 'force?'
284
+ else 'name*'
285
+ end)
242
286
  task flag do |_, args|
243
287
  package flag, args.to_a
244
288
  end
245
289
  when 'bump'
290
+ break unless version
291
+
246
292
  if flag == :version
247
293
  format_desc action, flag, 'version'
248
294
  task flag, [:version] do |_, args|
@@ -256,15 +302,16 @@ module Squared
256
302
  end
257
303
  end
258
304
  when 'publish'
259
- format_desc(action, flag, 'otp?,dry-run?,public|restricted?', before: flag == :tag ? 'tag' : nil)
305
+ format_desc(action, flag, 'otp?,d/ry-run?,p/ublic|r/estricted?',
306
+ before: ('tag' if flag == :tag))
260
307
  task flag do |_, args|
261
308
  args = args.to_a
262
309
  dryrun = true if args.delete('dry-run') || args.delete('d')
263
- if args.delete('public') || args.delete('p')
264
- access = 'public'
265
- elsif args.delete('restricted') || args.delete('r')
266
- access = 'restricted'
267
- end
310
+ access = if args.delete('public') || args.delete('p')
311
+ 'public'
312
+ elsif args.delete('restricted') || args.delete('r')
313
+ 'restricted'
314
+ end
268
315
  if flag == :latest
269
316
  otp = args.first
270
317
  else
@@ -272,6 +319,22 @@ module Squared
272
319
  end
273
320
  publish(flag, otp: otp, tag: tag, dryrun: dryrun, access: access)
274
321
  end
322
+ when 'tsc'
323
+ break unless @tsfile
324
+
325
+ format_desc(action, flag, 'opts*', "#{flag == :project ? 'before' : 'after'}": 'config?')
326
+ task flag do |_, args|
327
+ args = args.to_a
328
+ if flag == :project
329
+ project = if !args.empty? && exist?(args.first)
330
+ args.shift
331
+ else
332
+ @tsfile
333
+ end
334
+ end
335
+ watch = !(args.delete('watch') || args.delete('w')).nil?
336
+ tsc(*args, banner: true, project: project, build: flag == :build, watch: watch)
337
+ end
275
338
  end
276
339
  end
277
340
  end
@@ -282,8 +345,6 @@ module Squared
282
345
 
283
346
  def copy(from: 'build', into: 'node_modules', scope: nil, also: nil, create: nil, files: nil, workspace: false,
284
347
  link: false, force: false, override: false, sync: invoked_sync?('copy'), **kwargs)
285
- return if @copy == false
286
-
287
348
  glob = kwargs[:include]
288
349
  pass = kwargs[:exclude]
289
350
  if @copy && !override
@@ -300,6 +361,8 @@ module Squared
300
361
  create = @copy[:create] if @copy.key?(:create)
301
362
  glob = @copy[:include] if @copy.key?(:include)
302
363
  pass = @copy[:exclude] if @copy.key?(:exclude)
364
+ elsif @copy == false
365
+ return
303
366
  end
304
367
  items = []
305
368
  if build? && path != @workspace.home && @workspace.home?
@@ -321,13 +384,13 @@ module Squared
321
384
  dest = @workspace.root + dir
322
385
  @workspace.rev_clear(dest, sync: sync)
323
386
  when Symbol
324
- if (proj = @workspace.find(name: dir))
325
- @workspace.rev_clear(proj.name, sync: sync)
326
- dest = proj.path
327
- else
328
- log.warn message("copy project :#{dir}", hint: 'not found')
329
- dest = nil
330
- end
387
+ dest = if (proj = @workspace.find(name: dir))
388
+ @workspace.rev_clear(proj.name, sync: sync)
389
+ proj.path
390
+ else
391
+ log.warn message("copy project :#{dir}", hint: 'missing')
392
+ nil
393
+ end
331
394
  when Hash
332
395
  from = dir[:from] if dir.key?(:from)
333
396
  into = dir[:into] if dir.key?(:into)
@@ -345,7 +408,7 @@ module Squared
345
408
  dest = dir.path
346
409
  @workspace.rev_clear(dir.name, sync: sync)
347
410
  else
348
- raise_error "copy: given #{dir}"
411
+ raise_error TypeError, "unknown: #{dir}", hint: 'copy'
349
412
  end
350
413
  next unless from && dest&.directory?
351
414
 
@@ -363,14 +426,14 @@ module Squared
363
426
  .concat(Array(files))
364
427
  packed = true
365
428
  end
366
- to = dest.join(into, npmname)
367
- to.mkpath
368
- log.info "cp npm:#{npmname} #{to}"
429
+ base = dest.join(into, npmname)
430
+ base.mkpath
431
+ log.info "cp npm:#{npmname} #{base}"
369
432
  subdir = []
370
433
  errors = 0
371
434
  files.each do |file|
372
435
  s, d = file.is_a?(Array) ? file : [file, file]
373
- dest = to + d
436
+ dest = base + d
374
437
  unless subdir.include?((target = dest.dirname).to_s)
375
438
  target.mkpath
376
439
  subdir << target.to_s
@@ -383,7 +446,7 @@ module Squared
383
446
  rescue StandardError => e
384
447
  on_error e, :copy
385
448
  else
386
- puts message(to, subdir.size, files.size - errors) if verbose
449
+ puts message(base, subdir.size, files.size - errors) if verbose
387
450
  end
388
451
  next
389
452
  end
@@ -396,20 +459,19 @@ module Squared
396
459
 
397
460
  sub = if (proj = @workspace.find(entry))
398
461
  proj.packagename
399
- elsif (file = entry + 'package.json').exist?
462
+ elsif (file = entry + dependname).exist?
400
463
  begin
401
464
  doc = JSON.parse(file.read)
465
+ doc['name']
402
466
  rescue StandardError => e
403
467
  log.error e
404
468
  raise if exception
405
- else
406
- doc['name']
407
469
  end
408
470
  end
409
471
  if sub
410
472
  target << [entry, dest.join(into, sub)]
411
473
  else
412
- log.debug message("package.json in \"#{entry}\"", hint: 'not found')
474
+ log.debug message("#{dependname} in \"#{entry}\"", hint: 'missing')
413
475
  end
414
476
  end
415
477
  else
@@ -432,6 +494,8 @@ module Squared
432
494
  workspace.rev_clear(name, sync: sync)
433
495
  return update if !flag && env('NODE_UPDATE')
434
496
 
497
+ ws = env('NODE_WORKSPACES', equals: '0')
498
+ ci = option('ci')
435
499
  if (yarn = dependtype(:yarn)) > 0
436
500
  cmd = session 'yarn'
437
501
  if flag == :add
@@ -441,71 +505,91 @@ module Squared
441
505
  else
442
506
  cmd << 'install'
443
507
  cmd << '--ignore-engines' if yarn == 1 && !option('ignore-engines', equals: '0')
508
+ cmd << (yarn == 1 ? '--force' : '--check-cache') if option('force')
509
+ end
510
+ if nolockfile?('yarn')
511
+ cmd << '--no-lockfile'
512
+ elsif ci
513
+ if yarn == 1
514
+ cmd << '--frozen-lockfile'
515
+ else
516
+ cmd << '--immutable' << '--refresh-lockfile'
517
+ end
444
518
  end
445
519
  elsif pnpm?
446
520
  cmd = session 'pnpm'
447
521
  if flag == :add
448
522
  cmd << 'add' << "--save-#{save}"
449
523
  cmd << '--save-exact' if exact
450
- option('allow-build') { |val| cmd << quote_option('allow-build', val) }
451
524
  else
452
525
  cmd << 'install'
453
526
  append_platform
454
527
  end
455
- option('public-hoist-pattern') do |val|
456
- split_escape(val).each { |opt| cmd << shell_option('public-hoist-pattern', opt) }
528
+ option('public-hoist-pattern', ignore: false) do |val|
529
+ split_escape(val) { |opt| cmd << shell_option('public-hoist-pattern', opt) }
457
530
  end
458
- cmd << '--ignore-workspace' if env('NODE_WORKSPACES', equals: '0')
531
+ cmd << '--ignore-workspace' if ws
532
+ cmd << if option('force')
533
+ '--force'
534
+ elsif nolockfile?('pnpm')
535
+ '--no-lockfile'
536
+ elsif ci
537
+ '--frozen-lockfile'
538
+ end
459
539
  append_nocolor
460
540
  else
461
- cmd = session 'npm', 'install'
462
- if flag == :add
463
- cmd << "--save-#{save}"
464
- cmd << '--save-exact' if exact
541
+ cmd = session 'npm'
542
+ if ci
543
+ cmd << 'ci'
465
544
  else
466
- append_platform
545
+ cmd << 'install'
546
+ if flag == :add
547
+ cmd << "--save-#{save}"
548
+ cmd << '--save-exact' if exact
549
+ cmd.merge(packages.map { |pkg| shell_quote(pkg) })
550
+ else
551
+ append_platform
552
+ end
467
553
  end
468
- cmd << '--workspaces=false' if env('NODE_WORKSPACES', equals: '0')
469
- cmd << '--package-lock=false' if option('package-lock', equals: '0')
554
+ cmd << '--workspaces=false' if ws
555
+ cmd << '--package-lock=false' << 'save=false' if nolockfile?('npm')
470
556
  append_nocolor
471
557
  end
472
558
  append_loglevel
473
- cmd.merge(packages.map { |pkg| shell_quote(pkg) }) if flag == :add
474
- run(from: flag || :depend, sync: sync)
559
+ run(from: :depend, sync: sync)
475
560
  end
476
561
  end
477
562
 
478
563
  def outdated(flag = nil, opts = [], sync: invoked_sync?('outdated', flag))
479
- dryrun = opts.include?('dry-run') || opts.include?('d')
480
- if pnpm?
481
- cmd = session 'pnpm', 'outdated'
482
- dryrun ||= dryrun?('pnpm')
483
- else
484
- cmd = session 'npm', 'outdated'
485
- dryrun ||= dryrun?('npm')
486
- end
564
+ cmd = session(pnpm? ? 'pnpm' : 'npm', 'outdated')
565
+ dryrun = has_value?(opts, 'd', 'dry-run') || dryrun?
487
566
  unless dryrun
488
567
  log.info cmd.to_s
489
568
  on :first, :outdated
490
569
  end
491
- banner = format_banner(cmd.temp(dryrun ? '--dry-run' : nil))
570
+ banner = format_banner(cmd.temp(('--dry-run' if dryrun)))
492
571
  print_item banner if sync
493
572
  begin
494
- data = pwd_set(dryrun: dryrun) { `#{cmd.temp('--json', '--loglevel=error')}` }
573
+ data = pwd_set(dryrun: dryrun) { `#{cmd.temp('--json --loglevel=error')}` }
495
574
  doc = dependfile.read
496
575
  json = JSON.parse(doc)
497
- rescue StandardError => e
498
- on_error(e, :outdated, dryrun: dryrun)
499
- return
500
- else
501
576
  dep1 = json['dependencies'] || {}
502
577
  dep2 = json['devDependencies'] || {}
503
578
  target = json['name']
579
+ rescue StandardError => e
580
+ on_error(e, :outdated, dryrun: dryrun)
581
+ return
504
582
  end
505
583
  found = []
506
584
  avail = []
507
585
  rev = flag || (prod? ? :patch : :minor)
508
- inter = opts.include?('interactive') || opts.include?('i')
586
+ if sync && !stdin?
587
+ if has_value?(opts, 's', 'select') && !dryrun
588
+ items = []
589
+ elsif has_value?(opts, 'i', 'interactive')
590
+ ia = true
591
+ end
592
+ end
509
593
  unless data.empty?
510
594
  JSON.parse(data).each_pair do |key, val|
511
595
  val = val.find { |obj| obj['dependent'] == target } if val.is_a?(Array)
@@ -515,7 +599,7 @@ module Squared
515
599
  ch = file[0]
516
600
  if ch.match?(/[~^]/)
517
601
  file = file[1..-1]
518
- elsif inter && rev == :major
602
+ elsif ia && rev == :major
519
603
  major = true
520
604
  else
521
605
  avail << [key, file, latest, true]
@@ -543,14 +627,14 @@ module Squared
543
627
  b = f[2]
544
628
  c = w[0]
545
629
  d = w[2]
546
- case rev
547
- when :major
548
- upgrade = a == '0' ? c == '0' || c == '1' : true
549
- when :minor
550
- upgrade = ch == '^' && (a == '0' ? c == '0' && b == d : a == c)
551
- when :patch
552
- upgrade = a == c && b == d && f[4] != w[4]
553
- end
630
+ upgrade = case rev
631
+ when :major
632
+ a == '0' ? c == '0' || c == '1' : true
633
+ when :minor
634
+ ch == '^' && (a == '0' ? c == '0' && b == d : a == c)
635
+ when :patch
636
+ a == c && b == d && f[4] != w[4]
637
+ end
554
638
  if upgrade && !w[5]
555
639
  next if file == want
556
640
 
@@ -568,7 +652,7 @@ module Squared
568
652
  end
569
653
  pending = 0
570
654
  modified = 0
571
- size_col = ->(items, i) { items.map { |a| a[i] }.max_by(&:size).size }
655
+ size_col = ->(a, i) { a.map { |aa| aa[i] }.max_by(&:size).size }
572
656
  pad_ord = lambda do |val, ord|
573
657
  ret = val.succ.to_s
574
658
  ord.size > 9 ? ret.rjust(ord.size.to_s.size) : ret
@@ -588,52 +672,86 @@ module Squared
588
672
  if !found.empty?
589
673
  col1 = size_col.call(found, 0) + 4
590
674
  col2 = size_col.call(found, 1) + 4
675
+ packages = []
676
+ pat = ->(a) { /("#{Regexp.escape(a[0])}"\s*:\s*)"([~^])#{a[4] ? '?' : ''}#{Regexp.escape(a[1])}"/ }
677
+ edit = lambda do |a, pkg, mod|
678
+ packages << a[0]
679
+ modified += 1
680
+ "#{pkg}\"#{mod || (a[3] == 1 && a[4] ? '^' : '')}#{a[2]}\""
681
+ end
591
682
  found.each_with_index do |item, i|
592
683
  a, b, c, d, e = item
593
- f = inter && (rev != :major || e || semmajor?(item[5], item[6]))
594
- if f && !confirm_outdated(a, c, (d / 2.0).ceil, b, lock: e, col1: col1)
684
+ f = ia && (rev != :major || e || semmajor?(item[5], item[6]))
685
+ if f && !confirm_outdated(a, c, (d / 2.0).ceil, b, lock: e, col1: col1, timeout: 0)
595
686
  cur = -1
596
687
  else
597
688
  cur = modified
598
- doc.sub!(/("#{Regexp.escape(a)}"\s*:\s*)"([~^])#{e ? '?' : ''}#{Regexp.escape(b)}"/) do |capture|
689
+ doc.send(items ? :sub : :sub!, pat.call(item)) do |capture|
599
690
  if $2 == '~' && rev != :patch
600
691
  cur = -1
601
692
  pending += 1
602
693
  capture
603
694
  else
604
- modified += 1
605
- "#{$1}\"#{$2 || (d == 1 && e ? '^' : '')}#{c}\""
695
+ edit.call(item, $1, $2)
606
696
  end
607
697
  end
608
698
  end
609
699
  a = a.ljust(col1)
610
700
  b = b.ljust(col2)
611
- b = sub_style(b, styles: theme[:current]) if theme[:current]
701
+ b = sub_style(b, styles: theme[:current]) if theme[:current] && !stdin?
612
702
  c = if cur == -1
613
703
  'SKIP'
614
704
  elsif modified == cur
615
705
  'FAIL'
616
- elsif d == 1
617
- a = sub_style(a, styles: theme[:major])
618
- sub_style(c, :bold, styles: color(:green))
706
+ elsif stdin?
707
+ c
619
708
  else
620
- sub_style(c, pat: SEM_VER, styles: color(d == 3 ? :green : :yellow), index: d)
709
+ g = item
710
+ if d == 1
711
+ a = sub_style(a, styles: theme[:major])
712
+ sub_style(c, :bold, styles: color(:green))
713
+ else
714
+ sub_style(c, **opt_style(color(d == 3 ? :green : :yellow), SEM_VER, d))
715
+ end
621
716
  end
622
- puts "#{pad_ord.call(i, found)}. #{a + b + c}"
717
+ s = a + b + c
718
+ if !items
719
+ puts "#{pad_ord.call(i, found)}. #{s}"
720
+ elsif g
721
+ items << [s, g]
722
+ end
623
723
  end
624
724
  pending = avail.reduce(pending) { |a, b| a + (b[3] ? 0 : 1) }
625
- if dryrun || (modified == 0 && pending > 0)
626
- footer.call(modified, found.size)
725
+ if dryrun || (modified == 0 && (pending > 0 || (items && pending == 0)))
726
+ n = if items
727
+ puts(items.empty? ? 'No updates were found' : items.map(&:first))
728
+ items.size
729
+ else
730
+ found.size
731
+ end
732
+ footer.call(modified, n) unless n == 0
627
733
  elsif modified > 0
628
- modified = -1
629
- File.write(dependfile, doc)
630
- if opts.include?('update') || opts.include?('u') || option('update')
631
- update
632
- else
633
- footer.call(0, found.size)
734
+ if items
735
+ packages.clear
736
+ choice('Select a package', items.map(&:first), multiple: true, force: false, index: true, border: true)
737
+ .each do |n|
738
+ item = items[n.pred].last
739
+ doc.sub!(pat.call(item)) { edit.call(item, $1, $2) }
740
+ end
741
+ end
742
+ unless packages.empty?
743
+ File.write(dependfile, doc)
744
+ if sync && (opts.include?('diff') || option('diff'))
745
+ run(git_output('diff', shell_quote(dependfile)), banner: false)
746
+ end
747
+ if has_value?(opts, 'u', 'update') || option('update')
748
+ package(:update, packages: packages, from: :'outdated:update')
749
+ else
750
+ modified = -1
751
+ footer.call(0, found.size)
752
+ end
753
+ commit(:add, [dependname], pass: true)
634
754
  end
635
- printsucc
636
- commit(:add, ['package.json'], pass: true)
637
755
  end
638
756
  elsif !avail.empty?
639
757
  col1 = size_col.call(avail, 0) + 4
@@ -649,7 +767,7 @@ module Squared
649
767
  c = sub_style(c, styles: color(:green))
650
768
  pending += 1
651
769
  end
652
- puts "#{pad_ord.call(i, avail)}. #{a + c + b} (#{d ? 'locked' : 'latest'})"
770
+ puts "#{pad_ord.call(i, avail)}. #{(a + c + b).subhint(d ? 'locked' : 'latest')}"
653
771
  end
654
772
  footer.call(0, avail.size)
655
773
  else
@@ -663,10 +781,17 @@ module Squared
663
781
  end
664
782
 
665
783
  def publish(flag = nil, *, sync: invoked_sync?('publish', flag), otp: nil, tag: nil, access: nil, dryrun: nil)
666
- unless version && !read_packagemanager(:private)
667
- print_error('invalid task "publish"', subject: name, hint: version ? 'private' : nil)
784
+ if read_package('private')
785
+ ws = @children.select { |proj| proj.ref?(Node.ref) }
786
+ if ws.empty?
787
+ print_error('nothing to publish', subject: name, hint: 'private')
788
+ elsif confirm_basic('Publish workspace?', ws.map(&:name).join(', '), 'N')
789
+ ws.each { |proj| proj.publish(flag, sync: sync, otp: otp, tag: tag, access: access, dryrun: dryrun) }
790
+ end
668
791
  return
669
792
  end
793
+ return print_error('version not found', subject: name, hint: dependname) unless version
794
+
670
795
  cmd = session 'npm', 'publish'
671
796
  cmd << basic_option('otp', otp) if otp ||= option('otp')
672
797
  cmd << basic_option('tag', tag.tr(' ', '-')) if tag ||= option('tag')
@@ -680,134 +805,151 @@ module Squared
680
805
  if dryrun
681
806
  cmd << '--dry-run'
682
807
  else
683
- from = :publish
808
+ from = :'npm:publish'
684
809
  log.info cmd.to_s
685
810
  end
686
811
  if sync
687
- run(from: from, sync: sync, interactive: !dryrun && "Publish #{sub_style(npmname, styles: theme[:active])}")
812
+ run(from: from, sync: sync, interactive: !dryrun && ['Publish', 'N', npmname])
688
813
  else
689
814
  require 'open3'
690
815
  on :first, from
691
816
  pwd_set(from: from, dryrun: dryrun) do
692
- cmd = session_done cmd
693
- Open3.popen2e(cmd) do |_, out|
817
+ Open3.popen2e(cmd = session_done(cmd)) do |_, out|
694
818
  write_lines(out, banner: format_banner(cmd),
695
- sub: npmnotice + [pat: /^(.+)(Tarball .+)$/, styles: color(:bright_blue), index: 2])
819
+ sub: npmnotice(opt_style(color(:bright_blue), /^(.+)(Tarball .+)$/, 2)))
696
820
  end
697
821
  end
698
822
  on :last, from
699
823
  end
700
824
  end
701
825
 
702
- def package(flag, opts = [], from: nil)
826
+ def package(flag, opts = [], packages: [], from: nil)
703
827
  workspace.rev_clear(name)
704
828
  if (yarn = dependtype(:yarn)) > 0
705
- cmd = session 'yarn', if flag == :update
829
+ cmd = session 'yarn', case flag
830
+ when :update
706
831
  yarn == 1 ? 'upgrade' : 'up'
832
+ when :reinstall
833
+ if yarn == 1
834
+ remove_modules 'yarn' if opts.include?('force')
835
+ elsif opts.delete('force')
836
+ opts << 'check-cache'
837
+ end
838
+ opts << 'no-lockfile' if lockfile(true)
839
+ 'install'
707
840
  else
708
841
  yarn == 1 && flag == :dedupe ? 'install' : flag
709
842
  end
710
843
  op = OptionPartition.new(opts, if yarn == 1
711
844
  OPT_YARN.fetch(flag == :dedupe ? :install : flag, []) + OPT_YARN[:common]
712
845
  else
713
- OPT_BERRY[flag]
846
+ OPT_BERRY.fetch(flag, [])
714
847
  end, cmd, project: self)
715
- op << '--ignore-engines' if yarn == 1 && !option('ignore-engines', equals: '0')
848
+ if yarn == 1 && flag != :reinstall
849
+ op << '--no-lockfile' if nolockfile?('yarn')
850
+ op << '--ignore-engines' unless option('ignore-engines', equals: '0')
851
+ end
716
852
  op.clear
717
853
  append_loglevel
718
854
  else
719
- if pnpm?
720
- cmd = session 'pnpm', flag
721
- list = OPT_PNPM[:install_base] + OPT_PNPM.fetch(flag, []) + OPT_PNPM[:common]
722
- list.concat(OPT_PNPM[:install_as] + OPT_PNPM[:filter]) unless flag == :dedupe
723
- list.concat(OPT_PNPM[:cpu]) unless flag == :update
724
- no = OPT_PNPM[:"#{flag}_no"]
725
- else
726
- cmd = session 'npm', flag
727
- list = OPT_NPM[:install_base] + OPT_NPM.fetch(flag, []) + OPT_NPM[:common]
728
- list.concat(OPT_NPM[:install_as]) if flag == :install || flag == :update
729
- no = OPT_NPM[:install_no]
730
- end
731
- op = OptionPartition.new(opts, list, cmd, no: no, project: self)
855
+ args = if pnpm?
856
+ case flag
857
+ when :install, :update
858
+ opts << 'no-lockfile' if nolockfile?('pnpm')
859
+ when :reinstall
860
+ opts << 'force'
861
+ flag = :install
862
+ end
863
+ no = OPT_PNPM[:no][flag]
864
+ [
865
+ opts,
866
+ [flag, :install_a].yield_self do |out|
867
+ out.push(:install_b, :common_filter) unless flag == :dedupe
868
+ out << :common_cpu unless flag == :update
869
+ pnpmopts(*out)
870
+ end,
871
+ session('pnpm', flag)
872
+ ]
873
+ else
874
+ case flag
875
+ when :install, :update
876
+ opts.unshift('package-lock=false', 'save=false') if nolockfile?('npm')
877
+ when :reinstall
878
+ remove_modules 'npm' if opts.delete('force')
879
+ opts.unshift('package-lock=false') if lockfile(true)
880
+ flag = :install
881
+ end
882
+ no = OPT_NPM[:no][:install]
883
+ [
884
+ opts,
885
+ [flag, :install_a].yield_self do |out|
886
+ out << :install_b unless flag == :dedupe
887
+ npmopts(*out)
888
+ end,
889
+ session('npm', flag)
890
+ ]
891
+ end
892
+ op = OptionPartition.new(*args, no: no, project: self)
732
893
  op.each do |opt|
733
894
  if opt =~ op.values
734
895
  case $1
735
896
  when 'w', 'workspace'
736
897
  op << ($2.match?(%r{[\\/]}) ? quote_option($1, basepath($2)) : shell_option($1, $2))
737
898
  end
738
- elsif opt.include?('=')
899
+ elsif opt.include?('=') || opt.start_with?('-')
739
900
  op.errors << opt
740
901
  else
741
902
  op.found << opt
742
903
  end
743
904
  end
744
905
  op.swap
906
+ .concat(packages)
745
907
  append_platform if flag == :install
746
908
  append_nocolor
747
909
  append_loglevel
748
- if flag == :dedupe || pnpm?
910
+ if flag == :dedupe
749
911
  op.clear
750
912
  else
751
913
  op.append(quote: true)
752
914
  end
915
+ op.clear(errors: true)
753
916
  end
754
- op.clear(errors: true)
755
917
  run(from: from || :"package:#{flag}")
756
918
  end
757
919
 
758
920
  def bump(flag, val = nil)
759
- return unless (cur = version)
760
-
761
- if flag == :version
762
- return unless val
763
- else
764
- seg = semscan(cur, fill: false)
765
- case flag
766
- when :major
767
- if seg[0] != '0' || seg[2].nil?
768
- seg[0] = seg[0].succ
769
- seg[2] = '0'
770
- else
771
- seg[2] = seg[2].succ
772
- end
773
- seg[4] = '0'
774
- when :minor
775
- if seg[0] == '0'
776
- seg[4] &&= seg[4].succ
777
- else
778
- seg[2] = seg[2].succ
779
- seg[4] &&= '0'
780
- end
781
- when :patch
782
- seg[4] &&= seg[4].succ
783
- end
784
- return if (val = seg.join) == cur
921
+ if flag != :version
922
+ val = sembump version, flag
923
+ return if val == version
924
+ elsif !val
925
+ return
785
926
  end
786
- begin
787
- doc = dependfile.read
788
- if doc.sub!(/"version"\s*:\s*"#{cur}"/, "\"version\": \"#{val}\"")
789
- unless dryrun?
790
- dependfile.write(doc)
791
- log.info "bump version #{cur} to #{val} (#{flag})"
792
- on :first, :bump
793
- end
794
- if stdin?
795
- puts val
796
- elsif verbose
797
- major = flag == :major
798
- emphasize("version: #{val}", title: name, border: borderstyle, sub: [
799
- headerstyle,
800
- { pat: /\A(version:)( )(\S+)(.*)\z/, styles: color(major ? :green : :yellow), index: 3 },
801
- { pat: /\A(version:)(.*)\z/, styles: theme[major ? :major : :active] }
802
- ])
803
- end
804
- on :last, :bump unless dryrun?
805
- else
806
- raise_error('version not found', hint: dependfile)
927
+ doc = dependfile.read
928
+ if doc.sub!(/"version"\s*:\s*"#{version}"/, "\"version\": \"#{val}\"")
929
+ unless dryrun?
930
+ log.info "bump version #{version} to #{val.subhint(flag)}"
931
+ on :first, :bump
932
+ dependfile.write(doc)
807
933
  end
808
- rescue StandardError => e
809
- on_error e, :bump
934
+ if stdin?
935
+ puts val
936
+ elsif verbose
937
+ major = flag == :major
938
+ emphasize("version: #{val}", title: name, border: borderstyle, sub: [
939
+ headerstyle,
940
+ opt_style(color(major ? :green : :yellow), /\A(version:)( )(\S+)(.*)\z/, 3),
941
+ opt_style(theme[major ? :major : :active], /\A(version:)(.*)\z/)
942
+ ])
943
+ end
944
+ unless dryrun?
945
+ commit(:add, [dependname], pass: true)
946
+ on :last, :bump
947
+ end
948
+ else
949
+ raise_error 'version not found', hint: dependfile
810
950
  end
951
+ rescue StandardError => e
952
+ on_error e, :bump
811
953
  end
812
954
 
813
955
  def pack(opts = [])
@@ -818,8 +960,7 @@ module Squared
818
960
  op = OptionPartition.new(opts, OPT_BERRY[:pack], cmd, project: self)
819
961
  op.append?('out', Pathname.pwd + "#{project}-#{version}.tgz")
820
962
  else
821
- op = OptionPartition.new(opts, pnpm? ? OPT_PNPM[:pack] : OPT_NPM[:pack] + OPT_NPM[:common], cmd,
822
- project: self)
963
+ op = OptionPartition.new(opts, pnpm? ? OPT_PNPM[:pack] : npmopts(:pack), cmd, project: self)
823
964
  unless pnpm?
824
965
  op.each do |opt|
825
966
  next unless opt =~ op.values
@@ -837,15 +978,53 @@ module Squared
837
978
  run(from: :pack)
838
979
  end
839
980
 
981
+ def tsc(*args, with: nil, pass: PASS_NODE[:tsc], sync: true, banner: verbose?, from: :tsc, **kwargs)
982
+ session_apply(with, args: args, kwargs: kwargs, pass: pass) if with
983
+ p = kwargs[:project]
984
+ b = kwargs[:build]
985
+ w = kwargs[:watch]
986
+ list = OPT_TSC[:base] + OPT_TSC[:compiler]
987
+ cmd = session 'tsc', if p
988
+ quote_option 'p', basepath(p)
989
+ elsif b
990
+ list.concat(OPT_TSC[:build])
991
+ '-b'
992
+ end
993
+ if w
994
+ list.concat(OPT_TSC[:watch])
995
+ cmd << '-w'
996
+ end
997
+ op = OptionPartition.new(args, list, cmd, project: self, sep: ' ')
998
+ unless p
999
+ if b.is_a?(String)
1000
+ op.add_path(b)
1001
+ elsif w.is_a?(String)
1002
+ op.add_path(w)
1003
+ else
1004
+ op.exist?(add: true)
1005
+ end
1006
+ end
1007
+ op.clear
1008
+ cmd = session_done(op.target)
1009
+ print_run(cmd, banner, **kwargs)
1010
+ session 'npx', cmd
1011
+ start = time_epoch if kwargs.fetch(:verbose, verbose? && !stdin?)
1012
+ run(sync: sync, banner: banner, from: from).tap do |ret|
1013
+ next unless success?(ret, banner, start.nil?) && start
1014
+
1015
+ print_status(name, subject: 'tsc', start: start, from: :completed)
1016
+ end
1017
+ end
1018
+
840
1019
  def compose(target, opts = nil, script: false, args: nil, from: nil, **)
841
1020
  return unless target
842
1021
 
843
1022
  if script
844
1023
  ret = session dependbin, 'run'
845
- raise_error("#{dependbin} run: given #{target}", hint: from) unless append_any(target, build: true)
846
- append_any opts
1024
+ raise_error "#{dependbin} run: #{target}", hint: from unless append_any(target, build: true)
1025
+ append_any opts if opts
847
1026
  append_loglevel
848
- append_any(args, delim: true)
1027
+ append_any(args, delim: true) if args
849
1028
  ret
850
1029
  else
851
1030
  case target
@@ -856,7 +1035,7 @@ module Squared
856
1035
  when Enumerable
857
1036
  target.to_a.join(' ')
858
1037
  else
859
- raise_error("compose: given #{target}", hint: from)
1038
+ raise_error TypeError, "unknown: #{target}", hint: 'compose'
860
1039
  end
861
1040
  end
862
1041
  end
@@ -866,7 +1045,7 @@ module Squared
866
1045
  end
867
1046
 
868
1047
  def outdated?
869
- dependfile.exist? && !task_pass?('outdated')
1048
+ dependfile.exist?
870
1049
  end
871
1050
 
872
1051
  def update?
@@ -878,9 +1057,9 @@ module Squared
878
1057
  end
879
1058
 
880
1059
  def yarn?
881
- (@pm[:yarn] ||= if rootpath('yarn.lock', ascend: dependext).exist?
1060
+ (@pm[:yarn] ||= if rootpath('yarn.lock', ascend: dependroot).exist?
882
1061
  yarntype
883
- elsif (ver = read_packagemanager || read_install)
1062
+ elsif (ver = read_package || read_install)
884
1063
  if ver =~ /^yarn(?:@(\d)|$)/
885
1064
  $1 && $1.to_i > 1 ? yarntype : 1
886
1065
  else
@@ -899,9 +1078,9 @@ module Squared
899
1078
  end
900
1079
 
901
1080
  def pnpm?
902
- (@pm[:pnpm] ||= if rootpath('pnpm-lock.yaml', ascend: dependext).exist?
1081
+ (@pm[:pnpm] ||= if rootpath('pnpm-lock.yaml', ascend: dependroot).exist?
903
1082
  pnpmtype
904
- elsif (ver = read_packagemanager || read_install)
1083
+ elsif (ver = read_package || read_install)
905
1084
  ver.start_with?('pnpm') ? pnpmtype : 0
906
1085
  else
907
1086
  @pm[:__] == 'pnpm' ? pnpmtype : 0
@@ -910,9 +1089,9 @@ module Squared
910
1089
 
911
1090
  def workspaces?
912
1091
  if pnpm?
913
- basepath('pnpm-workspace.yaml').exist?
1092
+ exist?('pnpm-workspace.yaml')
914
1093
  else
915
- read_packagemanager(:workspaces).is_a?(Array)
1094
+ read_package('workspaces').is_a?(Array)
916
1095
  end
917
1096
  end
918
1097
 
@@ -940,76 +1119,76 @@ module Squared
940
1119
  end
941
1120
 
942
1121
  def version
943
- @version ||= read_packagemanager(:version)
1122
+ @version ||= read_package('version')
944
1123
  end
945
1124
 
946
1125
  def packagename
947
- read_packagemanager :name
1126
+ read_package 'name'
948
1127
  end
949
1128
 
950
1129
  def scripts
951
- @scripts ||= read_packagemanager(:scripts).yield_self { |ret| ret.is_a?(Hash) ? ret : {} }
1130
+ @scripts ||= read_package('scripts').yield_self { |ret| ret.is_a?(Hash) ? ret : {} }
952
1131
  end
953
1132
 
954
1133
  private
955
1134
 
956
- def read_packagemanager(key = nil, version: nil, update: false)
957
- if (key ? !@pm.key?(key) : @pm[:_].nil?) || update
1135
+ def read_package(key = 'packageManager', update: false)
1136
+ if !@pm.key?(key) || update
958
1137
  doc = JSON.parse(dependfile.read)
959
- if @pm[:_].nil?
960
- @pm[:_] = (val = doc['packageManager']) ? val[0, val.index('+') || val.size] : false
961
- @pm[:name] = doc['name']
962
- @pm[:scripts] = doc['scripts']
963
- @pm[:version] = doc['version']
964
- @pm[:private] = doc['private']
965
- @pm[:workspaces] = doc['workspaces']
1138
+ @pm[key] = case key
1139
+ when 'packageManager'
1140
+ if (val = doc['packageManager'])
1141
+ (n = val.index('+')) ? val[0, n] : val
1142
+ else
1143
+ false
1144
+ end
1145
+ else
1146
+ doc[key]
1147
+ end
1148
+ unless @pm[:_]
1149
+ %w[name scripts version private workspaces].each { |s| @pm[s] = doc[s] }
1150
+ @pm[:_] = true
966
1151
  end
967
- @pm[key] = doc[key.to_s] if key
968
1152
  end
969
1153
  rescue StandardError => e
970
1154
  log.debug e
971
- @pm[:_] = false
972
- nil
1155
+ @pm[key] = nil
973
1156
  else
974
- if key
975
- @pm[key]
976
- elsif (ret = @pm[:_]) && (!version || semgte?(ret[(ret.index('@') + 1)..-1], version))
977
- ret
978
- end
1157
+ @pm[key]
979
1158
  end
980
1159
 
981
1160
  def read_install
982
- return unless (ret = env('NODE_INSTALL'))
983
-
984
- if ret.include?(',')
985
- catch :found do
986
- split_escape(ret).each do |val|
987
- case val
988
- when /^yarn/
989
- next if yarntype(exist: true) == 0
990
- when /^pnpm/
991
- next if pnpmtype(exist: true) == 0
992
- when /^npm/
993
- nil
994
- else
995
- next
1161
+ env('NODE_INSTALL') do |ret|
1162
+ if ret.include?(',')
1163
+ catch :found do
1164
+ split_escape(ret) do |val|
1165
+ case val
1166
+ when /^yarn/
1167
+ next if yarntype(exist: true) == 0
1168
+ when /^pnpm/
1169
+ next if pnpmtype(exist: true) == 0
1170
+ when /^npm/
1171
+ nil
1172
+ else
1173
+ next
1174
+ end
1175
+ ret = val
1176
+ throw :found
996
1177
  end
997
- ret = val
998
- throw :found
1178
+ return
999
1179
  end
1000
- return
1001
1180
  end
1181
+ @pm['packageManager'] ||= ret
1182
+ ret
1002
1183
  end
1003
- @pm[:_] ||= ret
1004
- ret
1005
1184
  end
1006
1185
 
1007
1186
  def yarntype(exist: false)
1008
- if (rc = rootpath('.yarnrc.yml', ascend: dependext)).exist?
1187
+ if (rc = rootpath('.yarnrc.yml', ascend: dependroot)).exist?
1009
1188
  require 'yaml'
1010
1189
  doc = YAML.load_file(rc)
1011
1190
  doc.nodeLinker == 'node-modules' ? 2 : 3
1012
- elsif exist && !basepath('yarn.lock').exist?
1191
+ elsif exist && !exist?('yarn.lock')
1013
1192
  0
1014
1193
  else
1015
1194
  1
@@ -1023,7 +1202,7 @@ module Squared
1023
1202
 
1024
1203
  def pnpmtype(exist: false)
1025
1204
  require 'yaml'
1026
- doc = YAML.load_file(basepath('node_modules/.modules.yaml', ascend: dependext))
1205
+ doc = YAML.load_file(basepath('node_modules/.modules.yaml', ascend: dependroot))
1027
1206
  @pm['packageManager'] = doc['packageManager']
1028
1207
  case doc['nodeLinker']
1029
1208
  when 'hoisted'
@@ -1035,16 +1214,32 @@ module Squared
1035
1214
  end
1036
1215
  rescue StandardError => e
1037
1216
  if exist
1038
- %w[pnpm-lock.yaml pnpm-workspace.yaml].any? { |val| basepath(val).exist? } ? 4 : 0
1217
+ %w[pnpm-lock.yaml pnpm-workspace.yaml].any? { |val| exist?(val) } ? 4 : 0
1039
1218
  else
1040
1219
  log.debug e
1041
1220
  4
1042
1221
  end
1043
1222
  end
1044
1223
 
1224
+ def remove_modules(prefix = dependbin)
1225
+ modules = basepath 'node_modules'
1226
+ return false unless modules.directory? && (option('Y', prefix: prefix) || confirm_basic('Remove?', modules))
1227
+
1228
+ modules.rmtree
1229
+ rescue Timeout::Error => e
1230
+ puts
1231
+ print_error(e, hint: modules, pass: true)
1232
+ exit 1
1233
+ rescue StandardError => e
1234
+ print_error(e, pass: true)
1235
+ false
1236
+ else
1237
+ true
1238
+ end
1239
+
1045
1240
  def append_loglevel(target: @session)
1046
1241
  level = env('NODE_LOGLEVEL')
1047
- silent = verbosetype == 0 || level == 'silent'
1242
+ silent = !verbose || level == 'silent'
1048
1243
  return unless silent || level
1049
1244
 
1050
1245
  if yarn?
@@ -1078,25 +1273,57 @@ module Squared
1078
1273
  %w[cpu os libc].each { |name| option(name) { |val| target << basic_option(name, val) } }
1079
1274
  end
1080
1275
 
1081
- def dependext
1082
- 'package.json' if parent&.has?('outdated', Node.ref)
1276
+ def dependroot
1277
+ dependname if parent&.has?('outdated', Node.ref)
1083
1278
  end
1084
1279
 
1085
1280
  def npmname
1086
1281
  packagename || project
1087
1282
  end
1088
1283
 
1089
- def npmnotice
1284
+ def npmnotice(*args)
1090
1285
  [
1091
- { pat: /^(npm error )(code|\d+)(.+)$/, styles: color(:bright_cyan), index: 2 },
1092
- { pat: /^(npm )(error)(.*)$/, styles: color(:bright_red), index: 2 },
1093
- { pat: /^(npm )(notice)(.*)$/, styles: color(:bright_cyan), index: 2 },
1094
- { pat: /^(npm )(.+)$/, styles: :bold }
1095
- ]
1286
+ opt_style(color(:bright_cyan), /^(npm error )(code|\d+)(.+)$/, 2),
1287
+ opt_style(color(:bright_red), /^(npm )(error)(.*)$/, 2),
1288
+ opt_style(color(:bright_cyan), /^(npm )(notice)(.*)$/, 2),
1289
+ opt_style(:bold, /^(npm )(.+)$/)
1290
+ ].concat(args)
1291
+ end
1292
+
1293
+ def npmopts(*args)
1294
+ OPT_NPM[:common] + args.flat_map { |name| OPT_NPM.fetch(name, []) }
1295
+ end
1296
+
1297
+ def pnpmopts(*args)
1298
+ OPT_PNPM[:common] + args.flat_map { |name| OPT_PNPM.fetch(name, []) }
1299
+ end
1300
+
1301
+ def lockfile(delete = false)
1302
+ file = basepath(if yarn?
1303
+ 'yarn.lock'
1304
+ else
1305
+ pnpm? ? 'pnpm-lock.yaml' : 'package-lock.json'
1306
+ end)
1307
+ if file.exist?
1308
+ if delete
1309
+ file.delete
1310
+ return
1311
+ end
1312
+ file
1313
+ elsif (file = rootpath(file.basename, ascend: dependroot)).exist?
1314
+ file
1315
+ end
1316
+ rescue StandardError => e
1317
+ log.debug e
1318
+ file
1319
+ end
1320
+
1321
+ def nolockfile?(prefix = dependbin)
1322
+ option('package-lock', 'lockfile', prefix: prefix, equals: '0') || !option('no-lockfile', prefix: prefix).nil?
1096
1323
  end
1097
1324
 
1098
- def dryrun?(prefix = dependbin, **)
1099
- super || !option('dry-run', prefix: prefix).nil?
1325
+ def dryrun?(prefix = dependbin)
1326
+ super(target: @session, prefix: prefix)
1100
1327
  end
1101
1328
  end
1102
1329