squared 0.5.11 → 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
 
@@ -130,10 +172,9 @@ module Squared
130
172
  when 'add'
131
173
  format_desc action, nil, 'save?=prod|dev|optional|peer,name+'
132
174
  task action, [:save] do |_, args|
133
- save = param_guard(action, 'save', args: args, key: :save)
134
- exact = true if save.delete_prefix!('=')
135
- packages = case save
136
- when 'prod', 'dev', 'optional', 'peer'
175
+ packages = if args.save =~ /\A(=)?(prod|dev|optional|peer)\z/
176
+ exact = !$1.nil?
177
+ save = $2
137
178
  args.extras
138
179
  else
139
180
  save = 'prod'
@@ -147,21 +188,21 @@ module Squared
147
188
 
148
189
  format_desc action, nil, "script,opts*|#{indexchar}index+|#,pattern*"
149
190
  task action, [:script] do |_, args|
191
+ list = scripts.to_a
150
192
  if args.script == '#'
151
- format_list(scripts.to_a, "run[#{indexchar}N]", 'scripts', grep: args.extras, from: dependfile)
193
+ format_list(list, "run[#{indexchar}N]", 'scripts', grep: args.extras, from: dependfile)
152
194
  else
153
195
  args = param_guard(action, 'script', args: args.to_a)
154
196
  opts = []
155
197
  args.each do |val|
156
198
  if (n, extra = indexitem(val))
157
- if (item = list[n - 1])
158
- val = extra ? "#{item.first} #{extra}" : item.first
199
+ if (item = list[n.pred])
200
+ run compose([item.first, extra].compact.join(' '), script: true)
159
201
  elsif exception
160
202
  indexerror n, list
161
203
  else
162
204
  next log.warn "run script #{n} of #{list.size} (out of range)"
163
205
  end
164
- run compose(val, script: true)
165
206
  else
166
207
  opts << val
167
208
  end
@@ -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,19 +494,9 @@ module Squared
431
494
  workspace.rev_clear(name, sync: sync)
432
495
  return update if !flag && env('NODE_UPDATE')
433
496
 
434
- pnpm = pnpm?
435
- yarn = pnpm ? 0 : dependtype(:yarn)
436
- if @pm[:__] && !pnpm && yarn == 0
437
- case @pm[:__]
438
- when 'pnpm'
439
- pnpm = true
440
- when 'yarn'
441
- yarn = 1
442
- when 'berry'
443
- yarn = 2
444
- end
445
- end
446
- if yarn > 0
497
+ ws = env('NODE_WORKSPACES', equals: '0')
498
+ ci = option('ci')
499
+ if (yarn = dependtype(:yarn)) > 0
447
500
  cmd = session 'yarn'
448
501
  if flag == :add
449
502
  cmd << 'add'
@@ -452,8 +505,18 @@ module Squared
452
505
  else
453
506
  cmd << 'install'
454
507
  cmd << '--ignore-engines' if yarn == 1 && !option('ignore-engines', equals: '0')
508
+ cmd << (yarn == 1 ? '--force' : '--check-cache') if option('force')
455
509
  end
456
- elsif pnpm
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
518
+ end
519
+ elsif pnpm?
457
520
  cmd = session 'pnpm'
458
521
  if flag == :add
459
522
  cmd << 'add' << "--save-#{save}"
@@ -463,21 +526,33 @@ module Squared
463
526
  append_platform
464
527
  end
465
528
  option('public-hoist-pattern', ignore: false) do |val|
466
- split_escape(val).each { |opt| cmd << shell_option('public-hoist-pattern', opt) }
529
+ split_escape(val) { |opt| cmd << shell_option('public-hoist-pattern', opt) }
467
530
  end
468
- 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
469
539
  append_nocolor
470
540
  else
471
- cmd = session 'npm', 'install'
472
- if flag == :add
473
- cmd << "--save-#{save}"
474
- cmd << '--save-exact' if exact
475
- cmd.merge(packages.map { |pkg| shell_quote(pkg) })
541
+ cmd = session 'npm'
542
+ if ci
543
+ cmd << 'ci'
476
544
  else
477
- 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
478
553
  end
479
- cmd << '--workspaces=false' if env('NODE_WORKSPACES', equals: '0')
480
- 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')
481
556
  append_nocolor
482
557
  end
483
558
  append_loglevel
@@ -486,36 +561,35 @@ module Squared
486
561
  end
487
562
 
488
563
  def outdated(flag = nil, opts = [], sync: invoked_sync?('outdated', flag))
489
- dryrun = opts.include?('dry-run') || opts.include?('d')
490
- if pnpm?
491
- cmd = session 'pnpm', 'outdated'
492
- dryrun ||= dryrun?('pnpm')
493
- else
494
- cmd = session 'npm', 'outdated'
495
- dryrun ||= dryrun?('npm')
496
- end
564
+ cmd = session(pnpm? ? 'pnpm' : 'npm', 'outdated')
565
+ dryrun = has_value?(opts, 'd', 'dry-run') || dryrun?
497
566
  unless dryrun
498
567
  log.info cmd.to_s
499
568
  on :first, :outdated
500
569
  end
501
- banner = format_banner(cmd.temp(dryrun ? '--dry-run' : nil))
570
+ banner = format_banner(cmd.temp(('--dry-run' if dryrun)))
502
571
  print_item banner if sync
503
572
  begin
504
- data = pwd_set(dryrun: dryrun) { `#{cmd.temp('--json', '--loglevel=error')}` }
573
+ data = pwd_set(dryrun: dryrun) { `#{cmd.temp('--json --loglevel=error')}` }
505
574
  doc = dependfile.read
506
575
  json = JSON.parse(doc)
507
- rescue StandardError => e
508
- on_error(e, :outdated, dryrun: dryrun)
509
- return
510
- else
511
576
  dep1 = json['dependencies'] || {}
512
577
  dep2 = json['devDependencies'] || {}
513
578
  target = json['name']
579
+ rescue StandardError => e
580
+ on_error(e, :outdated, dryrun: dryrun)
581
+ return
514
582
  end
515
583
  found = []
516
584
  avail = []
517
585
  rev = flag || (prod? ? :patch : :minor)
518
- 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
519
593
  unless data.empty?
520
594
  JSON.parse(data).each_pair do |key, val|
521
595
  val = val.find { |obj| obj['dependent'] == target } if val.is_a?(Array)
@@ -525,7 +599,7 @@ module Squared
525
599
  ch = file[0]
526
600
  if ch.match?(/[~^]/)
527
601
  file = file[1..-1]
528
- elsif inter && rev == :major
602
+ elsif ia && rev == :major
529
603
  major = true
530
604
  else
531
605
  avail << [key, file, latest, true]
@@ -553,14 +627,14 @@ module Squared
553
627
  b = f[2]
554
628
  c = w[0]
555
629
  d = w[2]
556
- case rev
557
- when :major
558
- upgrade = a == '0' ? c == '0' || c == '1' : true
559
- when :minor
560
- upgrade = ch == '^' && (a == '0' ? c == '0' && b == d : a == c)
561
- when :patch
562
- upgrade = a == c && b == d && f[4] != w[4]
563
- 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
564
638
  if upgrade && !w[5]
565
639
  next if file == want
566
640
 
@@ -578,7 +652,7 @@ module Squared
578
652
  end
579
653
  pending = 0
580
654
  modified = 0
581
- 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 }
582
656
  pad_ord = lambda do |val, ord|
583
657
  ret = val.succ.to_s
584
658
  ord.size > 9 ? ret.rjust(ord.size.to_s.size) : ret
@@ -598,51 +672,86 @@ module Squared
598
672
  if !found.empty?
599
673
  col1 = size_col.call(found, 0) + 4
600
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
601
682
  found.each_with_index do |item, i|
602
683
  a, b, c, d, e = item
603
- f = inter && (rev != :major || e || semmajor?(item[5], item[6]))
604
- 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)
605
686
  cur = -1
606
687
  else
607
688
  cur = modified
608
- doc.sub!(/("#{Regexp.escape(a)}"\s*:\s*)"([~^])#{e ? '?' : ''}#{Regexp.escape(b)}"/) do |capture|
689
+ doc.send(items ? :sub : :sub!, pat.call(item)) do |capture|
609
690
  if $2 == '~' && rev != :patch
610
691
  cur = -1
611
692
  pending += 1
612
693
  capture
613
694
  else
614
- modified += 1
615
- "#{$1}\"#{$2 || (d == 1 && e ? '^' : '')}#{c}\""
695
+ edit.call(item, $1, $2)
616
696
  end
617
697
  end
618
698
  end
619
699
  a = a.ljust(col1)
620
700
  b = b.ljust(col2)
621
- b = sub_style(b, styles: theme[:current]) if theme[:current]
701
+ b = sub_style(b, styles: theme[:current]) if theme[:current] && !stdin?
622
702
  c = if cur == -1
623
703
  'SKIP'
624
704
  elsif modified == cur
625
705
  'FAIL'
626
- elsif d == 1
627
- a = sub_style(a, styles: theme[:major])
628
- sub_style(c, :bold, styles: color(:green))
706
+ elsif stdin?
707
+ c
629
708
  else
630
- 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
631
716
  end
632
- 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
633
723
  end
634
724
  pending = avail.reduce(pending) { |a, b| a + (b[3] ? 0 : 1) }
635
- if dryrun || (modified == 0 && pending > 0)
636
- 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
637
733
  elsif modified > 0
638
- modified = -1
639
- File.write(dependfile, doc)
640
- if opts.include?('update') || opts.include?('u') || option('update')
641
- update
642
- else
643
- 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)
644
754
  end
645
- commit(:add, refs: ['package.json'], pass: true)
646
755
  end
647
756
  elsif !avail.empty?
648
757
  col1 = size_col.call(avail, 0) + 4
@@ -658,7 +767,7 @@ module Squared
658
767
  c = sub_style(c, styles: color(:green))
659
768
  pending += 1
660
769
  end
661
- 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')}"
662
771
  end
663
772
  footer.call(0, avail.size)
664
773
  else
@@ -672,10 +781,17 @@ module Squared
672
781
  end
673
782
 
674
783
  def publish(flag = nil, *, sync: invoked_sync?('publish', flag), otp: nil, tag: nil, access: nil, dryrun: nil)
675
- unless version && !read_packagemanager(:private)
676
- 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
677
791
  return
678
792
  end
793
+ return print_error('version not found', subject: name, hint: dependname) unless version
794
+
679
795
  cmd = session 'npm', 'publish'
680
796
  cmd << basic_option('otp', otp) if otp ||= option('otp')
681
797
  cmd << basic_option('tag', tag.tr(' ', '-')) if tag ||= option('tag')
@@ -689,68 +805,105 @@ module Squared
689
805
  if dryrun
690
806
  cmd << '--dry-run'
691
807
  else
692
- from = :publish
808
+ from = :'npm:publish'
693
809
  log.info cmd.to_s
694
810
  end
695
811
  if sync
696
- 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])
697
813
  else
698
814
  require 'open3'
699
815
  on :first, from
700
816
  pwd_set(from: from, dryrun: dryrun) do
701
- cmd = session_done cmd
702
- Open3.popen2e(cmd) do |_, out|
817
+ Open3.popen2e(cmd = session_done(cmd)) do |_, out|
703
818
  write_lines(out, banner: format_banner(cmd),
704
- sub: npmnotice + [pat: /^(.+)(Tarball .+)$/, styles: color(:bright_blue), index: 2])
819
+ sub: npmnotice(opt_style(color(:bright_blue), /^(.+)(Tarball .+)$/, 2)))
705
820
  end
706
821
  end
707
822
  on :last, from
708
823
  end
709
824
  end
710
825
 
711
- def package(flag, opts = [], from: nil)
826
+ def package(flag, opts = [], packages: [], from: nil)
712
827
  workspace.rev_clear(name)
713
828
  if (yarn = dependtype(:yarn)) > 0
714
- cmd = session 'yarn', if flag == :update
829
+ cmd = session 'yarn', case flag
830
+ when :update
715
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'
716
840
  else
717
841
  yarn == 1 && flag == :dedupe ? 'install' : flag
718
842
  end
719
843
  op = OptionPartition.new(opts, if yarn == 1
720
844
  OPT_YARN.fetch(flag == :dedupe ? :install : flag, []) + OPT_YARN[:common]
721
845
  else
722
- OPT_BERRY[flag]
846
+ OPT_BERRY.fetch(flag, [])
723
847
  end, cmd, project: self)
724
- 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
725
852
  op.clear
726
853
  append_loglevel
727
854
  else
728
- if pnpm?
729
- cmd = session 'pnpm', flag
730
- list = OPT_PNPM[:install_base] + OPT_PNPM.fetch(flag, []) + OPT_PNPM[:common]
731
- list.concat(OPT_PNPM[:install_as] + OPT_PNPM[:filter]) unless flag == :dedupe
732
- list.concat(OPT_PNPM[:cpu]) unless flag == :update
733
- no = OPT_PNPM[:"#{flag}_no"]
734
- else
735
- cmd = session 'npm', flag
736
- list = OPT_NPM[:install_base] + OPT_NPM.fetch(flag, []) + OPT_NPM[:common]
737
- list.concat(OPT_NPM[:install_as]) unless flag == :dedupe
738
- no = OPT_NPM[:install_no]
739
- end
740
- 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)
741
893
  op.each do |opt|
742
894
  if opt =~ op.values
743
895
  case $1
744
896
  when 'w', 'workspace'
745
897
  op << ($2.match?(%r{[\\/]}) ? quote_option($1, basepath($2)) : shell_option($1, $2))
746
898
  end
747
- elsif opt.include?('=')
899
+ elsif opt.include?('=') || opt.start_with?('-')
748
900
  op.errors << opt
749
901
  else
750
902
  op.found << opt
751
903
  end
752
904
  end
753
905
  op.swap
906
+ .concat(packages)
754
907
  append_platform if flag == :install
755
908
  append_nocolor
756
909
  append_loglevel
@@ -765,58 +918,38 @@ module Squared
765
918
  end
766
919
 
767
920
  def bump(flag, val = nil)
768
- return unless (cur = version)
769
-
770
- if flag == :version
771
- return unless val
772
- else
773
- seg = semscan(cur, fill: false)
774
- case flag
775
- when :major
776
- if seg[0] != '0' || seg[2].nil?
777
- seg[0] = seg[0].succ
778
- seg[2] = '0'
779
- else
780
- seg[2] = seg[2].succ
781
- end
782
- seg[4] = '0'
783
- when :minor
784
- if seg[0] == '0'
785
- seg[4] &&= seg[4].succ
786
- else
787
- seg[2] = seg[2].succ
788
- seg[4] &&= '0'
789
- end
790
- when :patch
791
- seg[4] &&= seg[4].succ
792
- end
793
- 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
794
926
  end
795
- begin
796
- doc = dependfile.read
797
- if doc.sub!(/"version"\s*:\s*"#{cur}"/, "\"version\": \"#{val}\"")
798
- unless dryrun?
799
- dependfile.write(doc)
800
- log.info "bump version #{cur} to #{val} (#{flag})"
801
- on :first, :bump
802
- end
803
- if stdin?
804
- puts val
805
- elsif verbose
806
- major = flag == :major
807
- emphasize("version: #{val}", title: name, border: borderstyle, sub: [
808
- headerstyle,
809
- { pat: /\A(version:)( )(\S+)(.*)\z/, styles: color(major ? :green : :yellow), index: 3 },
810
- { pat: /\A(version:)(.*)\z/, styles: theme[major ? :major : :active] }
811
- ])
812
- end
813
- on :last, :bump unless dryrun?
814
- else
815
- 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)
816
933
  end
817
- rescue StandardError => e
818
- 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
819
950
  end
951
+ rescue StandardError => e
952
+ on_error e, :bump
820
953
  end
821
954
 
822
955
  def pack(opts = [])
@@ -827,8 +960,7 @@ module Squared
827
960
  op = OptionPartition.new(opts, OPT_BERRY[:pack], cmd, project: self)
828
961
  op.append?('out', Pathname.pwd + "#{project}-#{version}.tgz")
829
962
  else
830
- op = OptionPartition.new(opts, pnpm? ? OPT_PNPM[:pack] : OPT_NPM[:pack] + OPT_NPM[:common], cmd,
831
- project: self)
963
+ op = OptionPartition.new(opts, pnpm? ? OPT_PNPM[:pack] : npmopts(:pack), cmd, project: self)
832
964
  unless pnpm?
833
965
  op.each do |opt|
834
966
  next unless opt =~ op.values
@@ -846,15 +978,53 @@ module Squared
846
978
  run(from: :pack)
847
979
  end
848
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
+
849
1019
  def compose(target, opts = nil, script: false, args: nil, from: nil, **)
850
1020
  return unless target
851
1021
 
852
1022
  if script
853
1023
  ret = session dependbin, 'run'
854
- raise_error("#{dependbin} run: given #{target}", hint: from) unless append_any(target, build: true)
855
- append_any opts
1024
+ raise_error "#{dependbin} run: #{target}", hint: from unless append_any(target, build: true)
1025
+ append_any opts if opts
856
1026
  append_loglevel
857
- append_any(args, delim: true)
1027
+ append_any(args, delim: true) if args
858
1028
  ret
859
1029
  else
860
1030
  case target
@@ -865,7 +1035,7 @@ module Squared
865
1035
  when Enumerable
866
1036
  target.to_a.join(' ')
867
1037
  else
868
- raise_error("compose: given #{target}", hint: from)
1038
+ raise_error TypeError, "unknown: #{target}", hint: 'compose'
869
1039
  end
870
1040
  end
871
1041
  end
@@ -887,54 +1057,41 @@ module Squared
887
1057
  end
888
1058
 
889
1059
  def yarn?
890
- (@pm[:yarn] ||= if rootpath('yarn.lock', ascend: dependext).exist?
891
- if (rc = rootpath('.yarnrc.yml', ascend: dependext)).exist?
892
- begin
893
- require 'yaml'
894
- doc = YAML.load_file(rc)
895
- doc.nodeLinker == 'node-modules' ? 2 : 3
896
- rescue StandardError => e
897
- log.debug e
898
- 3
899
- end
1060
+ (@pm[:yarn] ||= if rootpath('yarn.lock', ascend: dependroot).exist?
1061
+ yarntype
1062
+ elsif (ver = read_package || read_install)
1063
+ if ver =~ /^yarn(?:@(\d)|$)/
1064
+ $1 && $1.to_i > 1 ? yarntype : 1
900
1065
  else
901
- 1
1066
+ 0
902
1067
  end
903
- elsif (ver = read_packagemanager || read_install)&.start_with?('yarn')
904
- ver == 'yarn' || ver.include?('@1') ? 1 : 3
905
1068
  else
906
- 0
1069
+ case @pm[:__]
1070
+ when 'yarn'
1071
+ 1
1072
+ when 'berry'
1073
+ yarntype
1074
+ else
1075
+ 0
1076
+ end
907
1077
  end) > 0
908
1078
  end
909
1079
 
910
1080
  def pnpm?
911
- (@pm[:pnpm] ||= if rootpath('pnpm-lock.yaml', ascend: dependext).exist?
912
- begin
913
- require 'yaml'
914
- doc = YAML.load_file(basepath('node_modules/.modules.yaml', ascend: dependext))
915
- @pm[:_] = doc['packageManager']
916
- case doc['nodeLinker']
917
- when 'hoisted'
918
- 1
919
- when 'pnp'
920
- 3
921
- else
922
- 4
923
- end
924
- rescue StandardError => e
925
- log.debug e
926
- 4
927
- end
1081
+ (@pm[:pnpm] ||= if rootpath('pnpm-lock.yaml', ascend: dependroot).exist?
1082
+ pnpmtype
1083
+ elsif (ver = read_package || read_install)
1084
+ ver.start_with?('pnpm') ? pnpmtype : 0
928
1085
  else
929
- (read_packagemanager || read_install)&.start_with?('pnpm') ? 4 : 0
1086
+ @pm[:__] == 'pnpm' ? pnpmtype : 0
930
1087
  end) > 0
931
1088
  end
932
1089
 
933
1090
  def workspaces?
934
1091
  if pnpm?
935
- basepath('pnpm-workspace.yaml').exist?
1092
+ exist?('pnpm-workspace.yaml')
936
1093
  else
937
- read_packagemanager(:workspaces).is_a?(Array)
1094
+ read_package('workspaces').is_a?(Array)
938
1095
  end
939
1096
  end
940
1097
 
@@ -962,46 +1119,122 @@ module Squared
962
1119
  end
963
1120
 
964
1121
  def version
965
- @version ||= read_packagemanager(:version)
1122
+ @version ||= read_package('version')
966
1123
  end
967
1124
 
968
1125
  def packagename
969
- read_packagemanager :name
1126
+ read_package 'name'
970
1127
  end
971
1128
 
972
1129
  def scripts
973
- @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 : {} }
974
1131
  end
975
1132
 
976
1133
  private
977
1134
 
978
- def read_packagemanager(key = nil, version: nil, update: false)
979
- if @pm[:_].nil? || update
1135
+ def read_package(key = 'packageManager', update: false)
1136
+ if !@pm.key?(key) || update
980
1137
  doc = JSON.parse(dependfile.read)
981
- @pm[:_] = (val = doc['packageManager']) ? val[0, val.index('+') || val.size] : false
982
- @pm[:name] = doc['name']
983
- @pm[:scripts] = doc['scripts']
984
- @pm[:version] = doc['version']
985
- @pm[:private] = doc['private']
986
- @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
1151
+ end
987
1152
  end
988
1153
  rescue StandardError => e
989
1154
  log.debug e
990
- @pm[:_] = false
991
- nil
1155
+ @pm[key] = nil
992
1156
  else
993
- if key
994
- @pm[key]
995
- elsif (ret = @pm[:_]) && (!version || semgte?(ret[(ret.index('@') + 1)..-1], version))
1157
+ @pm[key]
1158
+ end
1159
+
1160
+ def read_install
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
1177
+ end
1178
+ return
1179
+ end
1180
+ end
1181
+ @pm['packageManager'] ||= ret
996
1182
  ret
997
1183
  end
998
1184
  end
999
1185
 
1000
- def read_install
1001
- return unless (ret = env('NODE_INSTALL'))
1186
+ def yarntype(exist: false)
1187
+ if (rc = rootpath('.yarnrc.yml', ascend: dependroot)).exist?
1188
+ require 'yaml'
1189
+ doc = YAML.load_file(rc)
1190
+ doc.nodeLinker == 'node-modules' ? 2 : 3
1191
+ elsif exist && !exist?('yarn.lock')
1192
+ 0
1193
+ else
1194
+ 1
1195
+ end
1196
+ rescue StandardError => e
1197
+ return 0 if exist
1002
1198
 
1003
- @pm[:_] ||= ret if ret.include?('@')
1004
- ret
1199
+ log.debug e
1200
+ 3
1201
+ end
1202
+
1203
+ def pnpmtype(exist: false)
1204
+ require 'yaml'
1205
+ doc = YAML.load_file(basepath('node_modules/.modules.yaml', ascend: dependroot))
1206
+ @pm['packageManager'] = doc['packageManager']
1207
+ case doc['nodeLinker']
1208
+ when 'hoisted'
1209
+ 1
1210
+ when 'pnp'
1211
+ 3
1212
+ else
1213
+ 4
1214
+ end
1215
+ rescue StandardError => e
1216
+ if exist
1217
+ %w[pnpm-lock.yaml pnpm-workspace.yaml].any? { |val| exist?(val) } ? 4 : 0
1218
+ else
1219
+ log.debug e
1220
+ 4
1221
+ end
1222
+ end
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
1005
1238
  end
1006
1239
 
1007
1240
  def append_loglevel(target: @session)
@@ -1040,25 +1273,57 @@ module Squared
1040
1273
  %w[cpu os libc].each { |name| option(name) { |val| target << basic_option(name, val) } }
1041
1274
  end
1042
1275
 
1043
- def dependext
1044
- 'package.json' if parent&.has?('outdated', Node.ref)
1276
+ def dependroot
1277
+ dependname if parent&.has?('outdated', Node.ref)
1045
1278
  end
1046
1279
 
1047
1280
  def npmname
1048
1281
  packagename || project
1049
1282
  end
1050
1283
 
1051
- def npmnotice
1284
+ def npmnotice(*args)
1052
1285
  [
1053
- { pat: /^(npm error )(code|\d+)(.+)$/, styles: color(:bright_cyan), index: 2 },
1054
- { pat: /^(npm )(error)(.*)$/, styles: color(:bright_red), index: 2 },
1055
- { pat: /^(npm )(notice)(.*)$/, styles: color(:bright_cyan), index: 2 },
1056
- { pat: /^(npm )(.+)$/, styles: :bold }
1057
- ]
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?
1058
1323
  end
1059
1324
 
1060
- def dryrun?(prefix = dependbin, **)
1061
- super || !option('dry-run', prefix: prefix).nil?
1325
+ def dryrun?(prefix = dependbin)
1326
+ super(target: @session, prefix: prefix)
1062
1327
  end
1063
1328
  end
1064
1329