squared 0.5.12 → 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,36 +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,
8
+ common: %w[dry-run=!? loglevel=b include-workspace-root=!? workspaces=!? w|workspace=v].freeze,
9
9
  install: %w[package-lock-only=!? prefer-dedupe=!? cpu=b libc=b os=b].freeze,
10
- install_base: %w[audit=! bin-links=! fund=! ignore-scripts=!? install-links=!? package-lock=!
11
- 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 save-bundle save-dev save-optional save-peer save-prod before=q foreground-scripts=!?
14
- g|global=!? S|save=!? E|save-exact=!?].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
22
  common: %w[aggregate-output color no-color stream use-stderr C|dir=p loglevel=b w|workspace-root].freeze,
21
- cpu: %w[cpu=b libc=b os=b].freeze,
22
- filter: %w[fail-if-no-match changed-files-ignore-pattern=q filter=q filter-prod=q test-pattern=q].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,
23
26
  install: %w[fix-lockfile force ignore-pnpmfile ignore-workspace lockfile-only merge-git-branch-lockfiles
24
27
  optimistic-repeat-install no-hoist no-lockfile no-optional prefer-frozen-lockfile resolution-only
25
28
  shamefully-hoist side-effects-cache side-effects-cache-readonly s|silent strict-peer-dependencies
26
29
  use-running-store-server use-store-server child-concurrency=i hoist-pattern=q lockfile-dir=p
27
30
  modules-dir=p network-concurrency=i package-import-method=b public-hoist-pattern=q
28
31
  reporter=b].freeze,
29
- install_base: %w[global-dir ignore-scripts offline prefer-offline store-dir=p virtual-store-dir=p].freeze,
30
- install_no: %w[frozen-lockfile verify-store-integrity].freeze,
31
- install_as: %w[D|dev global-dir no-optional P|prod r|recursive].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,
32
34
  update: %w[g|global i|interactive L|latest depth=i].freeze,
33
35
  dedupe: %w[check].freeze,
34
36
  run: %w[if-present no-bail parallel r|recursive report-summary reporter-hide-prefix resume-from
35
37
  sequential].freeze,
36
38
  exec: %w[no-reporter-hide-prefix parallel r|recursive report-summary resume-from c|shell-mode].freeze,
37
- pack: %w[json pack-destination=p pack-gzip-level=i].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
38
43
  }.freeze
39
44
  OPT_YARN = {
40
45
  common: %w[check-files disable-pnp enable-pnp flat focus force frozen-lockfile json har ignore-engines
@@ -56,11 +61,45 @@ module Squared
56
61
  run: %w[B|binaries-only inspect inspect-brk T|top-level require=q].freeze,
57
62
  pack: %w[n|dry-run install-if-needed json o|out=p].freeze
58
63
  }.freeze
59
- 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
60
101
 
61
102
  class << self
62
- def populate(*); end
63
-
64
103
  def tasks
65
104
  %i[outdated update publish].freeze
66
105
  end
@@ -89,10 +128,11 @@ module Squared
89
128
  end
90
129
 
91
130
  subtasks({
92
- 'package' => %i[install dedupe update].freeze,
131
+ 'package' => %i[install reinstall dedupe update].freeze,
93
132
  'outdated' => %i[major minor patch].freeze,
94
133
  'bump' => %i[version major minor patch].freeze,
95
134
  'publish' => %i[latest tag].freeze,
135
+ 'tsc' => %i[project build].freeze,
96
136
  'add' => nil,
97
137
  'run' => nil,
98
138
  'exec' => nil,
@@ -100,7 +140,7 @@ module Squared
100
140
  'pack' => nil
101
141
  })
102
142
 
103
- def initialize(*, init: nil, asdf: 'nodejs', **kwargs)
143
+ def initialize(*, init: nil, ts: 'tsconfig.json', asdf: 'nodejs', **kwargs)
104
144
  super
105
145
  if @pass.include?(Node.ref)
106
146
  initialize_ref Node.ref
@@ -109,7 +149,9 @@ module Squared
109
149
  initialize_build(Node.ref, prod: prod?, **kwargs)
110
150
  initialize_env(**kwargs)
111
151
  end
112
- @dependfile = basepath 'package.json'
152
+ @dependname = 'package.json'
153
+ dependfile_set [@dependname]
154
+ @tsfile = basepath(ts).yield_self { |file| file if file.exist? }
113
155
  @pm = { __: init }
114
156
  end
115
157
 
@@ -154,7 +196,7 @@ module Squared
154
196
  opts = []
155
197
  args.each do |val|
156
198
  if (n, extra = indexitem(val))
157
- if (item = list[n - 1])
199
+ if (item = list[n.pred])
158
200
  run compose([item.first, extra].compact.join(' '), script: true)
159
201
  elsif exception
160
202
  indexerror n, list
@@ -170,13 +212,13 @@ module Squared
170
212
  list = if (yarn = dependtype(:yarn)) > 0
171
213
  yarn == 1 ? OPT_YARN[:run] + OPT_YARN[:common] : OPT_BERRY[:run]
172
214
  elsif pnpm?
173
- OPT_PNPM[:run] + OPT_PNPM[:filter] + OPT_PNPM[:common]
215
+ pnpmopts :run, :common_filter
174
216
  else
175
- OPT_NPM[:run] + OPT_NPM[:common]
217
+ npmopts :run
176
218
  end
177
- op = OptionPartition.new(opts, list, session(dependbin, 'run'), project: self)
178
- op << op.extras.shift
179
- 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)
180
222
  run(from: :run)
181
223
  end
182
224
  end
@@ -185,14 +227,14 @@ module Squared
185
227
  task action, [:package] do |_, args|
186
228
  if (package = args.package)
187
229
  args = args.extras
188
- if pnpm?
189
- pre = ->(ch) { "-#{ch}" if (ch = args.delete(ch)) }
190
- cmd = session 'pnpm', pre.call('r'), pre.call('c'), 'exec'
191
- list = OPT_PNPM[:exec] + OPT_PNPM[:filter] + OPT_PNPM[:common]
192
- else
193
- cmd = session 'npm', 'exec'
194
- list = OPT_NPM[:exec] + OPT_NPM[:common]
195
- 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
196
238
  op = OptionPartition.new(args, list, cmd, project: self)
197
239
  if op.empty?
198
240
  op << package
@@ -217,7 +259,7 @@ module Squared
217
259
  version = param_guard(action, 'version', args: args, key: :version)
218
260
  args = args.extras
219
261
  args << readline('Enter command', force: true) if args.empty?
220
- args.prepend(File.join(ENV['NVM_DIR'], 'nvm-exec'))
262
+ args.unshift(File.join(ENV['NVM_DIR'], 'nvm-exec'))
221
263
  run(args.join(' '), { 'NODE_VERSION' => version }, banner: false, from: :nvm)
222
264
  end
223
265
  when 'pack'
@@ -231,16 +273,22 @@ module Squared
231
273
  flags.each do |flag|
232
274
  case action
233
275
  when 'outdated'
234
- 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]
235
277
  task flag do |_, args|
236
278
  outdated flag, args.to_a
237
279
  end
238
280
  when 'package'
239
- 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)
240
286
  task flag do |_, args|
241
287
  package flag, args.to_a
242
288
  end
243
289
  when 'bump'
290
+ break unless version
291
+
244
292
  if flag == :version
245
293
  format_desc action, flag, 'version'
246
294
  task flag, [:version] do |_, args|
@@ -254,15 +302,16 @@ module Squared
254
302
  end
255
303
  end
256
304
  when 'publish'
257
- 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))
258
307
  task flag do |_, args|
259
308
  args = args.to_a
260
309
  dryrun = true if args.delete('dry-run') || args.delete('d')
261
- if args.delete('public') || args.delete('p')
262
- access = 'public'
263
- elsif args.delete('restricted') || args.delete('r')
264
- access = 'restricted'
265
- end
310
+ access = if args.delete('public') || args.delete('p')
311
+ 'public'
312
+ elsif args.delete('restricted') || args.delete('r')
313
+ 'restricted'
314
+ end
266
315
  if flag == :latest
267
316
  otp = args.first
268
317
  else
@@ -270,6 +319,22 @@ module Squared
270
319
  end
271
320
  publish(flag, otp: otp, tag: tag, dryrun: dryrun, access: access)
272
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
273
338
  end
274
339
  end
275
340
  end
@@ -319,13 +384,13 @@ module Squared
319
384
  dest = @workspace.root + dir
320
385
  @workspace.rev_clear(dest, sync: sync)
321
386
  when Symbol
322
- if (proj = @workspace.find(name: dir))
323
- @workspace.rev_clear(proj.name, sync: sync)
324
- dest = proj.path
325
- else
326
- log.warn message("copy project :#{dir}", hint: 'not found')
327
- dest = nil
328
- 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
329
394
  when Hash
330
395
  from = dir[:from] if dir.key?(:from)
331
396
  into = dir[:into] if dir.key?(:into)
@@ -343,7 +408,7 @@ module Squared
343
408
  dest = dir.path
344
409
  @workspace.rev_clear(dir.name, sync: sync)
345
410
  else
346
- raise_error "copy: given #{dir}"
411
+ raise_error TypeError, "unknown: #{dir}", hint: 'copy'
347
412
  end
348
413
  next unless from && dest&.directory?
349
414
 
@@ -361,14 +426,14 @@ module Squared
361
426
  .concat(Array(files))
362
427
  packed = true
363
428
  end
364
- to = dest.join(into, npmname)
365
- to.mkpath
366
- log.info "cp npm:#{npmname} #{to}"
429
+ base = dest.join(into, npmname)
430
+ base.mkpath
431
+ log.info "cp npm:#{npmname} #{base}"
367
432
  subdir = []
368
433
  errors = 0
369
434
  files.each do |file|
370
435
  s, d = file.is_a?(Array) ? file : [file, file]
371
- dest = to + d
436
+ dest = base + d
372
437
  unless subdir.include?((target = dest.dirname).to_s)
373
438
  target.mkpath
374
439
  subdir << target.to_s
@@ -381,7 +446,7 @@ module Squared
381
446
  rescue StandardError => e
382
447
  on_error e, :copy
383
448
  else
384
- puts message(to, subdir.size, files.size - errors) if verbose
449
+ puts message(base, subdir.size, files.size - errors) if verbose
385
450
  end
386
451
  next
387
452
  end
@@ -394,20 +459,19 @@ module Squared
394
459
 
395
460
  sub = if (proj = @workspace.find(entry))
396
461
  proj.packagename
397
- elsif (file = entry + 'package.json').exist?
462
+ elsif (file = entry + dependname).exist?
398
463
  begin
399
464
  doc = JSON.parse(file.read)
465
+ doc['name']
400
466
  rescue StandardError => e
401
467
  log.error e
402
468
  raise if exception
403
- else
404
- doc['name']
405
469
  end
406
470
  end
407
471
  if sub
408
472
  target << [entry, dest.join(into, sub)]
409
473
  else
410
- log.debug message("package.json in \"#{entry}\"", hint: 'not found')
474
+ log.debug message("#{dependname} in \"#{entry}\"", hint: 'missing')
411
475
  end
412
476
  end
413
477
  else
@@ -430,6 +494,8 @@ module Squared
430
494
  workspace.rev_clear(name, sync: sync)
431
495
  return update if !flag && env('NODE_UPDATE')
432
496
 
497
+ ws = env('NODE_WORKSPACES', equals: '0')
498
+ ci = option('ci')
433
499
  if (yarn = dependtype(:yarn)) > 0
434
500
  cmd = session 'yarn'
435
501
  if flag == :add
@@ -439,6 +505,16 @@ module Squared
439
505
  else
440
506
  cmd << 'install'
441
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
442
518
  end
443
519
  elsif pnpm?
444
520
  cmd = session 'pnpm'
@@ -450,21 +526,33 @@ module Squared
450
526
  append_platform
451
527
  end
452
528
  option('public-hoist-pattern', ignore: false) do |val|
453
- split_escape(val).each { |opt| cmd << shell_option('public-hoist-pattern', opt) }
529
+ split_escape(val) { |opt| cmd << shell_option('public-hoist-pattern', opt) }
454
530
  end
455
- 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
456
539
  append_nocolor
457
540
  else
458
- cmd = session 'npm', 'install'
459
- if flag == :add
460
- cmd << "--save-#{save}"
461
- cmd << '--save-exact' if exact
462
- cmd.merge(packages.map { |pkg| shell_quote(pkg) })
541
+ cmd = session 'npm'
542
+ if ci
543
+ cmd << 'ci'
463
544
  else
464
- 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
465
553
  end
466
- cmd << '--workspaces=false' if env('NODE_WORKSPACES', equals: '0')
467
- 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')
468
556
  append_nocolor
469
557
  end
470
558
  append_loglevel
@@ -473,36 +561,35 @@ module Squared
473
561
  end
474
562
 
475
563
  def outdated(flag = nil, opts = [], sync: invoked_sync?('outdated', flag))
476
- dryrun = opts.include?('dry-run') || opts.include?('d')
477
- if pnpm?
478
- cmd = session 'pnpm', 'outdated'
479
- dryrun ||= dryrun?('pnpm')
480
- else
481
- cmd = session 'npm', 'outdated'
482
- dryrun ||= dryrun?('npm')
483
- end
564
+ cmd = session(pnpm? ? 'pnpm' : 'npm', 'outdated')
565
+ dryrun = has_value?(opts, 'd', 'dry-run') || dryrun?
484
566
  unless dryrun
485
567
  log.info cmd.to_s
486
568
  on :first, :outdated
487
569
  end
488
- banner = format_banner(cmd.temp(dryrun ? '--dry-run' : nil))
570
+ banner = format_banner(cmd.temp(('--dry-run' if dryrun)))
489
571
  print_item banner if sync
490
572
  begin
491
- data = pwd_set(dryrun: dryrun) { `#{cmd.temp('--json', '--loglevel=error')}` }
573
+ data = pwd_set(dryrun: dryrun) { `#{cmd.temp('--json --loglevel=error')}` }
492
574
  doc = dependfile.read
493
575
  json = JSON.parse(doc)
494
- rescue StandardError => e
495
- on_error(e, :outdated, dryrun: dryrun)
496
- return
497
- else
498
576
  dep1 = json['dependencies'] || {}
499
577
  dep2 = json['devDependencies'] || {}
500
578
  target = json['name']
579
+ rescue StandardError => e
580
+ on_error(e, :outdated, dryrun: dryrun)
581
+ return
501
582
  end
502
583
  found = []
503
584
  avail = []
504
585
  rev = flag || (prod? ? :patch : :minor)
505
- 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
506
593
  unless data.empty?
507
594
  JSON.parse(data).each_pair do |key, val|
508
595
  val = val.find { |obj| obj['dependent'] == target } if val.is_a?(Array)
@@ -512,7 +599,7 @@ module Squared
512
599
  ch = file[0]
513
600
  if ch.match?(/[~^]/)
514
601
  file = file[1..-1]
515
- elsif inter && rev == :major
602
+ elsif ia && rev == :major
516
603
  major = true
517
604
  else
518
605
  avail << [key, file, latest, true]
@@ -540,14 +627,14 @@ module Squared
540
627
  b = f[2]
541
628
  c = w[0]
542
629
  d = w[2]
543
- case rev
544
- when :major
545
- upgrade = a == '0' ? c == '0' || c == '1' : true
546
- when :minor
547
- upgrade = ch == '^' && (a == '0' ? c == '0' && b == d : a == c)
548
- when :patch
549
- upgrade = a == c && b == d && f[4] != w[4]
550
- 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
551
638
  if upgrade && !w[5]
552
639
  next if file == want
553
640
 
@@ -565,7 +652,7 @@ module Squared
565
652
  end
566
653
  pending = 0
567
654
  modified = 0
568
- 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 }
569
656
  pad_ord = lambda do |val, ord|
570
657
  ret = val.succ.to_s
571
658
  ord.size > 9 ? ret.rjust(ord.size.to_s.size) : ret
@@ -585,51 +672,86 @@ module Squared
585
672
  if !found.empty?
586
673
  col1 = size_col.call(found, 0) + 4
587
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
588
682
  found.each_with_index do |item, i|
589
683
  a, b, c, d, e = item
590
- f = inter && (rev != :major || e || semmajor?(item[5], item[6]))
591
- 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)
592
686
  cur = -1
593
687
  else
594
688
  cur = modified
595
- doc.sub!(/("#{Regexp.escape(a)}"\s*:\s*)"([~^])#{e ? '?' : ''}#{Regexp.escape(b)}"/) do |capture|
689
+ doc.send(items ? :sub : :sub!, pat.call(item)) do |capture|
596
690
  if $2 == '~' && rev != :patch
597
691
  cur = -1
598
692
  pending += 1
599
693
  capture
600
694
  else
601
- modified += 1
602
- "#{$1}\"#{$2 || (d == 1 && e ? '^' : '')}#{c}\""
695
+ edit.call(item, $1, $2)
603
696
  end
604
697
  end
605
698
  end
606
699
  a = a.ljust(col1)
607
700
  b = b.ljust(col2)
608
- b = sub_style(b, styles: theme[:current]) if theme[:current]
701
+ b = sub_style(b, styles: theme[:current]) if theme[:current] && !stdin?
609
702
  c = if cur == -1
610
703
  'SKIP'
611
704
  elsif modified == cur
612
705
  'FAIL'
613
- elsif d == 1
614
- a = sub_style(a, styles: theme[:major])
615
- sub_style(c, :bold, styles: color(:green))
706
+ elsif stdin?
707
+ c
616
708
  else
617
- 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
618
716
  end
619
- 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
620
723
  end
621
724
  pending = avail.reduce(pending) { |a, b| a + (b[3] ? 0 : 1) }
622
- if dryrun || (modified == 0 && pending > 0)
623
- 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
624
733
  elsif modified > 0
625
- modified = -1
626
- File.write(dependfile, doc)
627
- if opts.include?('update') || opts.include?('u') || option('update')
628
- update
629
- else
630
- 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)
631
754
  end
632
- commit(:add, ['package.json'], pass: true)
633
755
  end
634
756
  elsif !avail.empty?
635
757
  col1 = size_col.call(avail, 0) + 4
@@ -645,7 +767,7 @@ module Squared
645
767
  c = sub_style(c, styles: color(:green))
646
768
  pending += 1
647
769
  end
648
- 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')}"
649
771
  end
650
772
  footer.call(0, avail.size)
651
773
  else
@@ -659,10 +781,17 @@ module Squared
659
781
  end
660
782
 
661
783
  def publish(flag = nil, *, sync: invoked_sync?('publish', flag), otp: nil, tag: nil, access: nil, dryrun: nil)
662
- unless version && !read_packagemanager(:private)
663
- 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
664
791
  return
665
792
  end
793
+ return print_error('version not found', subject: name, hint: dependname) unless version
794
+
666
795
  cmd = session 'npm', 'publish'
667
796
  cmd << basic_option('otp', otp) if otp ||= option('otp')
668
797
  cmd << basic_option('tag', tag.tr(' ', '-')) if tag ||= option('tag')
@@ -676,68 +805,105 @@ module Squared
676
805
  if dryrun
677
806
  cmd << '--dry-run'
678
807
  else
679
- from = :publish
808
+ from = :'npm:publish'
680
809
  log.info cmd.to_s
681
810
  end
682
811
  if sync
683
- 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])
684
813
  else
685
814
  require 'open3'
686
815
  on :first, from
687
816
  pwd_set(from: from, dryrun: dryrun) do
688
- cmd = session_done cmd
689
- Open3.popen2e(cmd) do |_, out|
817
+ Open3.popen2e(cmd = session_done(cmd)) do |_, out|
690
818
  write_lines(out, banner: format_banner(cmd),
691
- sub: npmnotice + [pat: /^(.+)(Tarball .+)$/, styles: color(:bright_blue), index: 2])
819
+ sub: npmnotice(opt_style(color(:bright_blue), /^(.+)(Tarball .+)$/, 2)))
692
820
  end
693
821
  end
694
822
  on :last, from
695
823
  end
696
824
  end
697
825
 
698
- def package(flag, opts = [], from: nil)
826
+ def package(flag, opts = [], packages: [], from: nil)
699
827
  workspace.rev_clear(name)
700
828
  if (yarn = dependtype(:yarn)) > 0
701
- cmd = session 'yarn', if flag == :update
829
+ cmd = session 'yarn', case flag
830
+ when :update
702
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'
703
840
  else
704
841
  yarn == 1 && flag == :dedupe ? 'install' : flag
705
842
  end
706
843
  op = OptionPartition.new(opts, if yarn == 1
707
844
  OPT_YARN.fetch(flag == :dedupe ? :install : flag, []) + OPT_YARN[:common]
708
845
  else
709
- OPT_BERRY[flag]
846
+ OPT_BERRY.fetch(flag, [])
710
847
  end, cmd, project: self)
711
- 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
712
852
  op.clear
713
853
  append_loglevel
714
854
  else
715
- if pnpm?
716
- cmd = session 'pnpm', flag
717
- list = OPT_PNPM[:install_base] + OPT_PNPM.fetch(flag, []) + OPT_PNPM[:common]
718
- list.concat(OPT_PNPM[:install_as] + OPT_PNPM[:filter]) unless flag == :dedupe
719
- list.concat(OPT_PNPM[:cpu]) unless flag == :update
720
- no = OPT_PNPM[:"#{flag}_no"]
721
- else
722
- cmd = session 'npm', flag
723
- list = OPT_NPM[:install_base] + OPT_NPM.fetch(flag, []) + OPT_NPM[:common]
724
- list.concat(OPT_NPM[:install_as]) unless flag == :dedupe
725
- no = OPT_NPM[:install_no]
726
- end
727
- 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)
728
893
  op.each do |opt|
729
894
  if opt =~ op.values
730
895
  case $1
731
896
  when 'w', 'workspace'
732
897
  op << ($2.match?(%r{[\\/]}) ? quote_option($1, basepath($2)) : shell_option($1, $2))
733
898
  end
734
- elsif opt.include?('=')
899
+ elsif opt.include?('=') || opt.start_with?('-')
735
900
  op.errors << opt
736
901
  else
737
902
  op.found << opt
738
903
  end
739
904
  end
740
905
  op.swap
906
+ .concat(packages)
741
907
  append_platform if flag == :install
742
908
  append_nocolor
743
909
  append_loglevel
@@ -752,58 +918,38 @@ module Squared
752
918
  end
753
919
 
754
920
  def bump(flag, val = nil)
755
- return unless (cur = version)
756
-
757
- if flag == :version
758
- return unless val
759
- else
760
- seg = semscan(cur, fill: false)
761
- case flag
762
- when :major
763
- if seg[0] != '0' || seg[2].nil?
764
- seg[0] = seg[0].succ
765
- seg[2] = '0'
766
- else
767
- seg[2] = seg[2].succ
768
- end
769
- seg[4] = '0'
770
- when :minor
771
- if seg[0] == '0'
772
- seg[4] &&= seg[4].succ
773
- else
774
- seg[2] = seg[2].succ
775
- seg[4] &&= '0'
776
- end
777
- when :patch
778
- seg[4] &&= seg[4].succ
779
- end
780
- 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
781
926
  end
782
- begin
783
- doc = dependfile.read
784
- if doc.sub!(/"version"\s*:\s*"#{cur}"/, "\"version\": \"#{val}\"")
785
- unless dryrun?
786
- dependfile.write(doc)
787
- log.info "bump version #{cur} to #{val} (#{flag})"
788
- on :first, :bump
789
- end
790
- if stdin?
791
- puts val
792
- elsif verbose
793
- major = flag == :major
794
- emphasize("version: #{val}", title: name, border: borderstyle, sub: [
795
- headerstyle,
796
- { pat: /\A(version:)( )(\S+)(.*)\z/, styles: color(major ? :green : :yellow), index: 3 },
797
- { pat: /\A(version:)(.*)\z/, styles: theme[major ? :major : :active] }
798
- ])
799
- end
800
- on :last, :bump unless dryrun?
801
- else
802
- 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)
803
933
  end
804
- rescue StandardError => e
805
- 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
806
950
  end
951
+ rescue StandardError => e
952
+ on_error e, :bump
807
953
  end
808
954
 
809
955
  def pack(opts = [])
@@ -814,8 +960,7 @@ module Squared
814
960
  op = OptionPartition.new(opts, OPT_BERRY[:pack], cmd, project: self)
815
961
  op.append?('out', Pathname.pwd + "#{project}-#{version}.tgz")
816
962
  else
817
- op = OptionPartition.new(opts, pnpm? ? OPT_PNPM[:pack] : OPT_NPM[:pack] + OPT_NPM[:common], cmd,
818
- project: self)
963
+ op = OptionPartition.new(opts, pnpm? ? OPT_PNPM[:pack] : npmopts(:pack), cmd, project: self)
819
964
  unless pnpm?
820
965
  op.each do |opt|
821
966
  next unless opt =~ op.values
@@ -833,15 +978,53 @@ module Squared
833
978
  run(from: :pack)
834
979
  end
835
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
+
836
1019
  def compose(target, opts = nil, script: false, args: nil, from: nil, **)
837
1020
  return unless target
838
1021
 
839
1022
  if script
840
1023
  ret = session dependbin, 'run'
841
- raise_error("#{dependbin} run: given #{target}", hint: from) unless append_any(target, build: true)
842
- append_any opts
1024
+ raise_error "#{dependbin} run: #{target}", hint: from unless append_any(target, build: true)
1025
+ append_any opts if opts
843
1026
  append_loglevel
844
- append_any(args, delim: true)
1027
+ append_any(args, delim: true) if args
845
1028
  ret
846
1029
  else
847
1030
  case target
@@ -852,7 +1035,7 @@ module Squared
852
1035
  when Enumerable
853
1036
  target.to_a.join(' ')
854
1037
  else
855
- raise_error("compose: given #{target}", hint: from)
1038
+ raise_error TypeError, "unknown: #{target}", hint: 'compose'
856
1039
  end
857
1040
  end
858
1041
  end
@@ -874,9 +1057,9 @@ module Squared
874
1057
  end
875
1058
 
876
1059
  def yarn?
877
- (@pm[:yarn] ||= if rootpath('yarn.lock', ascend: dependext).exist?
1060
+ (@pm[:yarn] ||= if rootpath('yarn.lock', ascend: dependroot).exist?
878
1061
  yarntype
879
- elsif (ver = read_packagemanager || read_install)
1062
+ elsif (ver = read_package || read_install)
880
1063
  if ver =~ /^yarn(?:@(\d)|$)/
881
1064
  $1 && $1.to_i > 1 ? yarntype : 1
882
1065
  else
@@ -895,9 +1078,9 @@ module Squared
895
1078
  end
896
1079
 
897
1080
  def pnpm?
898
- (@pm[:pnpm] ||= if rootpath('pnpm-lock.yaml', ascend: dependext).exist?
1081
+ (@pm[:pnpm] ||= if rootpath('pnpm-lock.yaml', ascend: dependroot).exist?
899
1082
  pnpmtype
900
- elsif (ver = read_packagemanager || read_install)
1083
+ elsif (ver = read_package || read_install)
901
1084
  ver.start_with?('pnpm') ? pnpmtype : 0
902
1085
  else
903
1086
  @pm[:__] == 'pnpm' ? pnpmtype : 0
@@ -906,9 +1089,9 @@ module Squared
906
1089
 
907
1090
  def workspaces?
908
1091
  if pnpm?
909
- basepath('pnpm-workspace.yaml').exist?
1092
+ exist?('pnpm-workspace.yaml')
910
1093
  else
911
- read_packagemanager(:workspaces).is_a?(Array)
1094
+ read_package('workspaces').is_a?(Array)
912
1095
  end
913
1096
  end
914
1097
 
@@ -936,76 +1119,76 @@ module Squared
936
1119
  end
937
1120
 
938
1121
  def version
939
- @version ||= read_packagemanager(:version)
1122
+ @version ||= read_package('version')
940
1123
  end
941
1124
 
942
1125
  def packagename
943
- read_packagemanager :name
1126
+ read_package 'name'
944
1127
  end
945
1128
 
946
1129
  def scripts
947
- @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 : {} }
948
1131
  end
949
1132
 
950
1133
  private
951
1134
 
952
- def read_packagemanager(key = nil, version: nil, update: false)
953
- if (key ? !@pm.key?(key) : @pm[:_].nil?) || update
1135
+ def read_package(key = 'packageManager', update: false)
1136
+ if !@pm.key?(key) || update
954
1137
  doc = JSON.parse(dependfile.read)
955
- if @pm[:_].nil?
956
- @pm[:_] = (val = doc['packageManager']) ? val[0, val.index('+') || val.size] : false
957
- @pm[:name] = doc['name']
958
- @pm[:scripts] = doc['scripts']
959
- @pm[:version] = doc['version']
960
- @pm[:private] = doc['private']
961
- @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
962
1151
  end
963
- @pm[key] = doc[key.to_s] if key
964
1152
  end
965
1153
  rescue StandardError => e
966
1154
  log.debug e
967
- @pm[:_] = false
968
- nil
1155
+ @pm[key] = nil
969
1156
  else
970
- if key
971
- @pm[key]
972
- elsif (ret = @pm[:_]) && (!version || semgte?(ret[(ret.index('@') + 1)..-1], version))
973
- ret
974
- end
1157
+ @pm[key]
975
1158
  end
976
1159
 
977
1160
  def read_install
978
- return unless (ret = env('NODE_INSTALL'))
979
-
980
- if ret.include?(',')
981
- catch :found do
982
- split_escape(ret).each do |val|
983
- case val
984
- when /^yarn/
985
- next if yarntype(exist: true) == 0
986
- when /^pnpm/
987
- next if pnpmtype(exist: true) == 0
988
- when /^npm/
989
- nil
990
- else
991
- 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
992
1177
  end
993
- ret = val
994
- throw :found
1178
+ return
995
1179
  end
996
- return
997
1180
  end
1181
+ @pm['packageManager'] ||= ret
1182
+ ret
998
1183
  end
999
- @pm[:_] ||= ret
1000
- ret
1001
1184
  end
1002
1185
 
1003
1186
  def yarntype(exist: false)
1004
- if (rc = rootpath('.yarnrc.yml', ascend: dependext)).exist?
1187
+ if (rc = rootpath('.yarnrc.yml', ascend: dependroot)).exist?
1005
1188
  require 'yaml'
1006
1189
  doc = YAML.load_file(rc)
1007
1190
  doc.nodeLinker == 'node-modules' ? 2 : 3
1008
- elsif exist && !basepath('yarn.lock').exist?
1191
+ elsif exist && !exist?('yarn.lock')
1009
1192
  0
1010
1193
  else
1011
1194
  1
@@ -1019,7 +1202,7 @@ module Squared
1019
1202
 
1020
1203
  def pnpmtype(exist: false)
1021
1204
  require 'yaml'
1022
- doc = YAML.load_file(basepath('node_modules/.modules.yaml', ascend: dependext))
1205
+ doc = YAML.load_file(basepath('node_modules/.modules.yaml', ascend: dependroot))
1023
1206
  @pm['packageManager'] = doc['packageManager']
1024
1207
  case doc['nodeLinker']
1025
1208
  when 'hoisted'
@@ -1031,13 +1214,29 @@ module Squared
1031
1214
  end
1032
1215
  rescue StandardError => e
1033
1216
  if exist
1034
- %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
1035
1218
  else
1036
1219
  log.debug e
1037
1220
  4
1038
1221
  end
1039
1222
  end
1040
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
+
1041
1240
  def append_loglevel(target: @session)
1042
1241
  level = env('NODE_LOGLEVEL')
1043
1242
  silent = !verbose || level == 'silent'
@@ -1074,25 +1273,57 @@ module Squared
1074
1273
  %w[cpu os libc].each { |name| option(name) { |val| target << basic_option(name, val) } }
1075
1274
  end
1076
1275
 
1077
- def dependext
1078
- 'package.json' if parent&.has?('outdated', Node.ref)
1276
+ def dependroot
1277
+ dependname if parent&.has?('outdated', Node.ref)
1079
1278
  end
1080
1279
 
1081
1280
  def npmname
1082
1281
  packagename || project
1083
1282
  end
1084
1283
 
1085
- def npmnotice
1284
+ def npmnotice(*args)
1086
1285
  [
1087
- { pat: /^(npm error )(code|\d+)(.+)$/, styles: color(:bright_cyan), index: 2 },
1088
- { pat: /^(npm )(error)(.*)$/, styles: color(:bright_red), index: 2 },
1089
- { pat: /^(npm )(notice)(.*)$/, styles: color(:bright_cyan), index: 2 },
1090
- { pat: /^(npm )(.+)$/, styles: :bold }
1091
- ]
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?
1092
1323
  end
1093
1324
 
1094
- def dryrun?(prefix = dependbin, **)
1095
- super || !option('dry-run', prefix: prefix).nil?
1325
+ def dryrun?(prefix = dependbin)
1326
+ super(target: @session, prefix: prefix)
1096
1327
  end
1097
1328
  end
1098
1329