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