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