squared 0.5.22 → 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=!? force=!? loglevel=b include-workspace-root=!? 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,19 +494,26 @@ 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
- if !flag && yarn > 1 && prod?
439
- cmd << 'workspaces focus --all --production'
440
- elsif flag == :add
501
+ if flag == :add
441
502
  cmd << 'add'
442
503
  cmd << "--#{save}" unless save == 'prod'
443
504
  cmd << '--exact' if exact
444
505
  else
445
506
  cmd << 'install'
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
446
513
  if yarn == 1
447
- cmd << '--production' if prod?
448
- cmd << '--ignore-engines' unless option('ignore-engines', equals: '0')
514
+ cmd << '--frozen-lockfile'
515
+ else
516
+ cmd << '--immutable' << '--refresh-lockfile'
449
517
  end
450
518
  end
451
519
  elsif pnpm?
@@ -453,68 +521,75 @@ module Squared
453
521
  if flag == :add
454
522
  cmd << 'add' << "--save-#{save}"
455
523
  cmd << '--save-exact' if exact
456
- option('allow-build') { |val| cmd << quote_option('allow-build', val) }
457
524
  else
458
525
  cmd << 'install'
459
- cmd << '--prod' if prod?
460
526
  append_platform
461
527
  end
462
- option('public-hoist-pattern') do |val|
463
- 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) }
464
530
  end
465
- cmd << '--ignore-workspace' if env('NODE_WORKSPACES', equals: '0')
466
- 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
467
539
  append_nocolor
468
540
  else
469
- cmd = session 'npm', 'install'
470
- if flag == :add
471
- cmd << "--save-#{save}"
472
- cmd << '--save-exact' if exact
541
+ cmd = session 'npm'
542
+ if ci
543
+ cmd << 'ci'
473
544
  else
474
- cmd << '--include=prod' if prod?
475
- 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
476
553
  end
477
- cmd << '--workspaces=false' if env('NODE_WORKSPACES', equals: '0')
478
- 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')
479
556
  append_nocolor
480
557
  end
481
558
  append_loglevel
482
- cmd.merge(packages.map { |pkg| shell_quote(pkg) }) if flag == :add
483
- run(from: flag || :depend, sync: sync)
559
+ run(from: :depend, sync: sync)
484
560
  end
485
561
  end
486
562
 
487
563
  def outdated(flag = nil, opts = [], sync: invoked_sync?('outdated', flag))
488
- dryrun = opts.include?('dry-run') || opts.include?('d')
489
- if pnpm?
490
- cmd = session 'pnpm', 'outdated'
491
- dryrun ||= dryrun?('pnpm')
492
- else
493
- cmd = session 'npm', 'outdated'
494
- dryrun ||= dryrun?('npm')
495
- end
564
+ cmd = session(pnpm? ? 'pnpm' : 'npm', 'outdated')
565
+ dryrun = has_value?(opts, 'd', 'dry-run') || dryrun?
496
566
  unless dryrun
497
567
  log.info cmd.to_s
498
568
  on :first, :outdated
499
569
  end
500
- banner = format_banner(cmd.temp(dryrun ? '--dry-run' : nil))
570
+ banner = format_banner(cmd.temp(('--dry-run' if dryrun)))
501
571
  print_item banner if sync
502
572
  begin
503
- data = pwd_set(dryrun: dryrun) { `#{cmd.temp('--json', '--loglevel=error')}` }
573
+ data = pwd_set(dryrun: dryrun) { `#{cmd.temp('--json --loglevel=error')}` }
504
574
  doc = dependfile.read
505
575
  json = JSON.parse(doc)
506
- rescue StandardError => e
507
- on_error(e, :outdated, dryrun: dryrun)
508
- return
509
- else
510
576
  dep1 = json['dependencies'] || {}
511
577
  dep2 = json['devDependencies'] || {}
512
578
  target = json['name']
579
+ rescue StandardError => e
580
+ on_error(e, :outdated, dryrun: dryrun)
581
+ return
513
582
  end
514
583
  found = []
515
584
  avail = []
516
585
  rev = flag || (prod? ? :patch : :minor)
517
- 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
518
593
  unless data.empty?
519
594
  JSON.parse(data).each_pair do |key, val|
520
595
  val = val.find { |obj| obj['dependent'] == target } if val.is_a?(Array)
@@ -524,7 +599,7 @@ module Squared
524
599
  ch = file[0]
525
600
  if ch.match?(/[~^]/)
526
601
  file = file[1..-1]
527
- elsif inter && rev == :major
602
+ elsif ia && rev == :major
528
603
  major = true
529
604
  else
530
605
  avail << [key, file, latest, true]
@@ -552,14 +627,14 @@ module Squared
552
627
  b = f[2]
553
628
  c = w[0]
554
629
  d = w[2]
555
- case rev
556
- when :major
557
- upgrade = a == '0' ? c == '0' || c == '1' : true
558
- when :minor
559
- upgrade = ch == '^' && (a == '0' ? c == '0' && b == d : a == c)
560
- when :patch
561
- upgrade = a == c && b == d && f[4] != w[4]
562
- 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
563
638
  if upgrade && !w[5]
564
639
  next if file == want
565
640
 
@@ -577,7 +652,7 @@ module Squared
577
652
  end
578
653
  pending = 0
579
654
  modified = 0
580
- 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 }
581
656
  pad_ord = lambda do |val, ord|
582
657
  ret = val.succ.to_s
583
658
  ord.size > 9 ? ret.rjust(ord.size.to_s.size) : ret
@@ -597,52 +672,86 @@ module Squared
597
672
  if !found.empty?
598
673
  col1 = size_col.call(found, 0) + 4
599
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
600
682
  found.each_with_index do |item, i|
601
683
  a, b, c, d, e = item
602
- f = inter && (rev != :major || e || semmajor?(item[5], item[6]))
603
- 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)
604
686
  cur = -1
605
687
  else
606
688
  cur = modified
607
- doc.sub!(/("#{Regexp.escape(a)}"\s*:\s*)"([~^])#{e ? '?' : ''}#{Regexp.escape(b)}"/) do |capture|
689
+ doc.send(items ? :sub : :sub!, pat.call(item)) do |capture|
608
690
  if $2 == '~' && rev != :patch
609
691
  cur = -1
610
692
  pending += 1
611
693
  capture
612
694
  else
613
- modified += 1
614
- "#{$1}\"#{$2 || (d == 1 && e ? '^' : '')}#{c}\""
695
+ edit.call(item, $1, $2)
615
696
  end
616
697
  end
617
698
  end
618
699
  a = a.ljust(col1)
619
700
  b = b.ljust(col2)
620
- b = sub_style(b, styles: theme[:current]) if theme[:current]
701
+ b = sub_style(b, styles: theme[:current]) if theme[:current] && !stdin?
621
702
  c = if cur == -1
622
703
  'SKIP'
623
704
  elsif modified == cur
624
705
  'FAIL'
625
- elsif d == 1
626
- a = sub_style(a, styles: theme[:major])
627
- sub_style(c, :bold, styles: color(:green))
706
+ elsif stdin?
707
+ c
628
708
  else
629
- 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
630
716
  end
631
- 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
632
723
  end
633
724
  pending = avail.reduce(pending) { |a, b| a + (b[3] ? 0 : 1) }
634
- if dryrun || (modified == 0 && pending > 0)
635
- 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
636
733
  elsif modified > 0
637
- modified = -1
638
- File.write(dependfile, doc)
639
- if opts.include?('update') || opts.include?('u') || option('update')
640
- update
641
- else
642
- 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)
643
754
  end
644
- printsucc
645
- commit(:add, ['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,135 +805,151 @@ 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(sync: sync, from: from, 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|
703
- write_lines(out,
704
- banner: format_banner(cmd),
705
- sub: npmnotice + [{ pat: /^(.+)(Tarball .+)$/, styles: color(:bright_blue), index: 2 }])
817
+ Open3.popen2e(cmd = session_done(cmd)) do |_, out|
818
+ write_lines(out, banner: format_banner(cmd),
819
+ sub: npmnotice(opt_style(color(:bright_blue), /^(.+)(Tarball .+)$/, 2)))
706
820
  end
707
821
  end
708
822
  on :last, from
709
823
  end
710
824
  end
711
825
 
712
- def package(flag, opts = [], from: nil)
826
+ def package(flag, opts = [], packages: [], from: nil)
713
827
  workspace.rev_clear(name)
714
828
  if (yarn = dependtype(:yarn)) > 0
715
- cmd = session 'yarn', if flag == :update
829
+ cmd = session 'yarn', case flag
830
+ when :update
716
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'
717
840
  else
718
841
  yarn == 1 && flag == :dedupe ? 'install' : flag
719
842
  end
720
843
  op = OptionPartition.new(opts, if yarn == 1
721
844
  OPT_YARN.fetch(flag == :dedupe ? :install : flag, []) + OPT_YARN[:common]
722
845
  else
723
- OPT_BERRY[flag]
846
+ OPT_BERRY.fetch(flag, [])
724
847
  end, cmd, project: self)
725
- 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
726
852
  op.clear
727
853
  append_loglevel
728
854
  else
729
- if pnpm?
730
- cmd = session 'pnpm', flag
731
- list = OPT_PNPM[:install_base] + OPT_PNPM.fetch(flag, []) + OPT_PNPM[:common]
732
- list.concat(OPT_PNPM[:install_as] + OPT_PNPM[:filter]) unless flag == :dedupe
733
- list.concat(OPT_PNPM[:cpu]) unless flag == :update
734
- no = OPT_PNPM[:"#{flag}_no"]
735
- else
736
- cmd = session 'npm', flag
737
- list = OPT_NPM[:install_base] + OPT_NPM.fetch(flag, []) + OPT_NPM[:common]
738
- list.concat(OPT_NPM[:install_as]) if flag == :install || flag == :update
739
- no = OPT_NPM[:install_no]
740
- end
741
- 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)
742
893
  op.each do |opt|
743
894
  if opt =~ op.values
744
895
  case $1
745
896
  when 'w', 'workspace'
746
897
  op << ($2.match?(%r{[\\/]}) ? quote_option($1, basepath($2)) : shell_option($1, $2))
747
898
  end
748
- elsif opt.include?('=')
899
+ elsif opt.include?('=') || opt.start_with?('-')
749
900
  op.errors << opt
750
901
  else
751
902
  op.found << opt
752
903
  end
753
904
  end
754
905
  op.swap
906
+ .concat(packages)
755
907
  append_platform if flag == :install
756
908
  append_nocolor
757
909
  append_loglevel
758
- if flag == :dedupe || pnpm?
910
+ if flag == :dedupe
759
911
  op.clear
760
912
  else
761
913
  op.append(quote: true)
762
914
  end
915
+ op.clear(errors: true)
763
916
  end
764
- op.clear(errors: true)
765
917
  run(from: from || :"package:#{flag}")
766
918
  end
767
919
 
768
920
  def bump(flag, val = nil)
769
- return unless (cur = version)
770
-
771
- if flag == :version
772
- return unless val
773
- else
774
- seg = semscan(cur, fill: false)
775
- case flag
776
- when :major
777
- if seg[0] != '0' || seg[2].nil?
778
- seg[0] = seg[0].succ
779
- seg[2] = '0'
780
- else
781
- seg[2] = seg[2].succ
782
- end
783
- seg[4] = '0'
784
- when :minor
785
- if seg[0] == '0'
786
- seg[4] &&= seg[4].succ
787
- else
788
- seg[2] = seg[2].succ
789
- seg[4] &&= '0'
790
- end
791
- when :patch
792
- seg[4] &&= seg[4].succ
793
- end
794
- 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
795
926
  end
796
- begin
797
- doc = dependfile.read
798
- if doc.sub!(/"version"\s*:\s*"#{cur}"/, "\"version\": \"#{val}\"")
799
- unless dryrun?
800
- dependfile.write(doc)
801
- log.info "bump version #{cur} to #{val} (#{flag})"
802
- on :first, :bump
803
- end
804
- if stdin?
805
- puts val
806
- elsif verbose
807
- major = flag == :major
808
- emphasize("version: #{val}", title: name, border: borderstyle, sub: [
809
- headerstyle,
810
- { pat: /\A(version:)( )(\S+)(.*)\z/, styles: color(major ? :green : :yellow), index: 3 },
811
- { pat: /\A(version:)(.*)\z/, styles: theme[major ? :major : :active] }
812
- ])
813
- end
814
- on :last, :bump unless dryrun?
815
- else
816
- 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)
817
933
  end
818
- rescue StandardError => e
819
- on_error(e, :bump, dryrun: dryrun?)
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
820
950
  end
951
+ rescue StandardError => e
952
+ on_error e, :bump
821
953
  end
822
954
 
823
955
  def pack(opts = [])
@@ -828,8 +960,7 @@ module Squared
828
960
  op = OptionPartition.new(opts, OPT_BERRY[:pack], cmd, project: self)
829
961
  op.append?('out', Pathname.pwd + "#{project}-#{version}.tgz")
830
962
  else
831
- op = OptionPartition.new(opts, pnpm? ? OPT_PNPM[:pack] : OPT_NPM[:pack] + OPT_NPM[:common], cmd,
832
- project: self)
963
+ op = OptionPartition.new(opts, pnpm? ? OPT_PNPM[:pack] : npmopts(:pack), cmd, project: self)
833
964
  unless pnpm?
834
965
  op.each do |opt|
835
966
  next unless opt =~ op.values
@@ -847,15 +978,53 @@ module Squared
847
978
  run(from: :pack)
848
979
  end
849
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
+
850
1019
  def compose(target, opts = nil, script: false, args: nil, from: nil, **)
851
1020
  return unless target
852
1021
 
853
1022
  if script
854
1023
  ret = session dependbin, 'run'
855
- raise_error("#{dependbin} run: given #{target}", hint: from) unless append_any(target, build: true)
856
- append_any opts
1024
+ raise_error "#{dependbin} run: #{target}", hint: from unless append_any(target, build: true)
1025
+ append_any opts if opts
857
1026
  append_loglevel
858
- append_any(args, delim: true)
1027
+ append_any(args, delim: true) if args
859
1028
  ret
860
1029
  else
861
1030
  case target
@@ -866,7 +1035,7 @@ module Squared
866
1035
  when Enumerable
867
1036
  target.to_a.join(' ')
868
1037
  else
869
- raise_error("compose: given #{target}", hint: from)
1038
+ raise_error TypeError, "unknown: #{target}", hint: 'compose'
870
1039
  end
871
1040
  end
872
1041
  end
@@ -876,7 +1045,7 @@ module Squared
876
1045
  end
877
1046
 
878
1047
  def outdated?
879
- dependfile.exist? && !task_pass?('outdated')
1048
+ dependfile.exist?
880
1049
  end
881
1050
 
882
1051
  def update?
@@ -888,9 +1057,9 @@ module Squared
888
1057
  end
889
1058
 
890
1059
  def yarn?
891
- (@pm[:yarn] ||= if rootpath('yarn.lock', ascend: dependext).exist?
1060
+ (@pm[:yarn] ||= if rootpath('yarn.lock', ascend: dependroot).exist?
892
1061
  yarntype
893
- elsif (ver = read_packagemanager || read_install)
1062
+ elsif (ver = read_package || read_install)
894
1063
  if ver =~ /^yarn(?:@(\d)|$)/
895
1064
  $1 && $1.to_i > 1 ? yarntype : 1
896
1065
  else
@@ -909,9 +1078,9 @@ module Squared
909
1078
  end
910
1079
 
911
1080
  def pnpm?
912
- (@pm[:pnpm] ||= if rootpath('pnpm-lock.yaml', ascend: dependext).exist?
1081
+ (@pm[:pnpm] ||= if rootpath('pnpm-lock.yaml', ascend: dependroot).exist?
913
1082
  pnpmtype
914
- elsif (ver = read_packagemanager || read_install)
1083
+ elsif (ver = read_package || read_install)
915
1084
  ver.start_with?('pnpm') ? pnpmtype : 0
916
1085
  else
917
1086
  @pm[:__] == 'pnpm' ? pnpmtype : 0
@@ -920,9 +1089,9 @@ module Squared
920
1089
 
921
1090
  def workspaces?
922
1091
  if pnpm?
923
- basepath('pnpm-workspace.yaml').exist?
1092
+ exist?('pnpm-workspace.yaml')
924
1093
  else
925
- read_packagemanager(:workspaces).is_a?(Array)
1094
+ read_package('workspaces').is_a?(Array)
926
1095
  end
927
1096
  end
928
1097
 
@@ -950,76 +1119,76 @@ module Squared
950
1119
  end
951
1120
 
952
1121
  def version
953
- @version ||= read_packagemanager(:version)
1122
+ @version ||= read_package('version')
954
1123
  end
955
1124
 
956
1125
  def packagename
957
- read_packagemanager :name
1126
+ read_package 'name'
958
1127
  end
959
1128
 
960
1129
  def scripts
961
- @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 : {} }
962
1131
  end
963
1132
 
964
1133
  private
965
1134
 
966
- def read_packagemanager(key = nil, version: nil, update: false)
967
- if (key ? !@pm.key?(key) : @pm[:_].nil?) || update
1135
+ def read_package(key = 'packageManager', update: false)
1136
+ if !@pm.key?(key) || update
968
1137
  doc = JSON.parse(dependfile.read)
969
- if @pm[:_].nil?
970
- @pm[:_] = (val = doc['packageManager']) ? val[0, val.index('+') || val.size] : false
971
- @pm[:name] = doc['name']
972
- @pm[:scripts] = doc['scripts']
973
- @pm[:version] = doc['version']
974
- @pm[:private] = doc['private']
975
- @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
976
1151
  end
977
- @pm[key] = doc[key.to_s] if key
978
1152
  end
979
1153
  rescue StandardError => e
980
1154
  log.debug e
981
- @pm[:_] = false
982
- nil
1155
+ @pm[key] = nil
983
1156
  else
984
- if key
985
- @pm[key]
986
- elsif (ret = @pm[:_]) && (!version || semgte?(ret[(ret.index('@') + 1)..-1], version))
987
- ret
988
- end
1157
+ @pm[key]
989
1158
  end
990
1159
 
991
1160
  def read_install
992
- return unless (ret = env('NODE_INSTALL'))
993
-
994
- if ret.include?(',')
995
- catch :found do
996
- split_escape(ret).each do |val|
997
- case val
998
- when /^yarn/
999
- next if yarntype(exist: true) == 0
1000
- when /^pnpm/
1001
- next if pnpmtype(exist: true) == 0
1002
- when /^npm/
1003
- nil
1004
- else
1005
- 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
1006
1177
  end
1007
- ret = val
1008
- throw :found
1178
+ return
1009
1179
  end
1010
- return
1011
1180
  end
1181
+ @pm['packageManager'] ||= ret
1182
+ ret
1012
1183
  end
1013
- @pm[:_] ||= ret
1014
- ret
1015
1184
  end
1016
1185
 
1017
1186
  def yarntype(exist: false)
1018
- if (rc = rootpath('.yarnrc.yml', ascend: dependext)).exist?
1187
+ if (rc = rootpath('.yarnrc.yml', ascend: dependroot)).exist?
1019
1188
  require 'yaml'
1020
1189
  doc = YAML.load_file(rc)
1021
1190
  doc.nodeLinker == 'node-modules' ? 2 : 3
1022
- elsif exist && !basepath('yarn.lock').exist?
1191
+ elsif exist && !exist?('yarn.lock')
1023
1192
  0
1024
1193
  else
1025
1194
  1
@@ -1033,7 +1202,7 @@ module Squared
1033
1202
 
1034
1203
  def pnpmtype(exist: false)
1035
1204
  require 'yaml'
1036
- doc = YAML.load_file(basepath('node_modules/.modules.yaml', ascend: dependext))
1205
+ doc = YAML.load_file(basepath('node_modules/.modules.yaml', ascend: dependroot))
1037
1206
  @pm['packageManager'] = doc['packageManager']
1038
1207
  case doc['nodeLinker']
1039
1208
  when 'hoisted'
@@ -1045,16 +1214,32 @@ module Squared
1045
1214
  end
1046
1215
  rescue StandardError => e
1047
1216
  if exist
1048
- %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
1049
1218
  else
1050
1219
  log.debug e
1051
1220
  4
1052
1221
  end
1053
1222
  end
1054
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
+
1055
1240
  def append_loglevel(target: @session)
1056
1241
  level = env('NODE_LOGLEVEL')
1057
- silent = verbosetype == 0 || level == 'silent'
1242
+ silent = !verbose || level == 'silent'
1058
1243
  return unless silent || level
1059
1244
 
1060
1245
  if yarn?
@@ -1088,25 +1273,57 @@ module Squared
1088
1273
  %w[cpu os libc].each { |name| option(name) { |val| target << basic_option(name, val) } }
1089
1274
  end
1090
1275
 
1091
- def dependext
1092
- 'package.json' if parent&.has?('outdated', Node.ref)
1276
+ def dependroot
1277
+ dependname if parent&.has?('outdated', Node.ref)
1093
1278
  end
1094
1279
 
1095
1280
  def npmname
1096
1281
  packagename || project
1097
1282
  end
1098
1283
 
1099
- def npmnotice
1284
+ def npmnotice(*args)
1100
1285
  [
1101
- { pat: /^(npm error )(code|\d+)(.+)$/, styles: color(:bright_cyan), index: 2 },
1102
- { pat: /^(npm )(error)(.*)$/, styles: color(:bright_red), index: 2 },
1103
- { pat: /^(npm )(notice)(.*)$/, styles: color(:bright_cyan), index: 2 },
1104
- { pat: /^(npm )(.+)$/, styles: :bold }
1105
- ]
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?
1106
1323
  end
1107
1324
 
1108
- def dryrun?(prefix = dependbin, **)
1109
- super || !option('dry-run', prefix: prefix).nil?
1325
+ def dryrun?(prefix = dependbin)
1326
+ super(target: @session, prefix: prefix)
1110
1327
  end
1111
1328
  end
1112
1329