squared 0.0.1 → 0.0.3

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.
@@ -4,60 +4,80 @@ module Squared
4
4
  module Repo
5
5
  module Project
6
6
  class Node < Git
7
+ REF = :node
8
+ private_constant :REF
9
+
7
10
  class << self
11
+ def populate(*); end
12
+
8
13
  def tasks
9
14
  nil
10
15
  end
11
16
 
12
- def to_sym
13
- :node
14
- end
15
-
16
17
  def prod?
17
18
  ENV['NODE_ENV'] == 'production'
18
19
  end
19
20
 
20
- def is_a?(path)
21
- if path.is_a?(::String) || path.is_a?(::Pathname)
22
- Pathname.new(path).join('package.json').exist?
21
+ def is_a?(val)
22
+ if (val = as_path(val))
23
+ val.join('package.json').exist?
23
24
  else
24
25
  super
25
26
  end
26
27
  end
27
28
  end
28
29
 
29
- @@tasks[:node] = {
30
- install: %i[force dedupe],
31
- outdated: %i[major minor patch]
30
+ @@tasks[REF] = {
31
+ install: %i[force dedupe frozen],
32
+ outdated: %i[major minor patch],
33
+ run: nil
32
34
  }.freeze
33
35
 
34
- def initialize(name, project, workspace, **kwargs)
36
+ def initialize(name, path, workspace, *, **kwargs)
35
37
  super
36
- @clean ||= ['build/']
38
+ initialize_script(REF)
39
+ if (opts = env('BUILD', strict: true))
40
+ raise ArgumentError, message("BUILD_#{@name.upcase}", opts) if @output[0].is_a?(::Array)
41
+
42
+ @output[1] = opts
43
+ else
44
+ @output[1] = (@script && @script[:run]) || workspace.script
45
+ end
37
46
  @dev = workspace.bool_match(env('BUILD', suffix: 'DEV'), kwargs.delete(:dev))
38
47
  @prod = workspace.bool_match(env('BUILD', suffix: 'PROD'), kwargs.delete(:prod))
39
- @package = {}
48
+ @pm = {}
40
49
  end
41
50
 
42
51
  def populate(*)
43
52
  super
44
53
  return unless outdated?
45
54
 
46
- namespace @name do
47
- @@tasks[:node].each do |action, flags|
48
- namespace action do
49
- flags.each do |flag|
50
- case action
51
- when :install
52
- desc format_desc(action, flag)
53
- task flag do
54
- install flag
55
- end
56
- when :outdated
57
- desc format_desc(action, flag, %w[prune dry-run], req: 'opts?')
58
- task flag, [:opts] do |_, args|
59
- opts = collect_args(args, :opts)
60
- outdated(flag, opts: opts)
55
+ namespace name do
56
+ @@tasks[REF].each do |action, flags|
57
+ if flags.nil?
58
+ case action
59
+ when :run
60
+ desc format_desc(action, nil, 'command+')
61
+ task action, [:command] do |_, args|
62
+ command = collect_args(args, :command)
63
+ guard_params(action, 'command', args: command)
64
+ run_script command
65
+ end
66
+ end
67
+ else
68
+ namespace action do
69
+ flags.each do |flag|
70
+ case action
71
+ when :install
72
+ desc format_desc(action, flag)
73
+ task flag do
74
+ install flag
75
+ end
76
+ when :outdated
77
+ desc format_desc(action, flag, %w[prune dry-run], req: 'opts?')
78
+ task flag, [:opts] do |_, args|
79
+ outdated(flag, opts: collect_args(args, :opts))
80
+ end
61
81
  end
62
82
  end
63
83
  end
@@ -66,26 +86,24 @@ module Squared
66
86
  end
67
87
  end
68
88
 
69
- def copy(from: 'build', glob: '**/*', basedir: 'node_modules', into: nil, also: [], override: false)
89
+ def copy(from: 'build', glob: '**/*', subdir: 'node_modules', into: nil, also: [], override: false)
70
90
  if @copy && !override
71
91
  return super if @copy.is_a?(::String)
72
92
 
73
93
  from = @copy[:from] if @copy.key?(:from)
74
94
  glob = @copy[:glob] if @copy.key?(:glob)
75
- basedir = @copy[:basedir] if @copy.key?(:basedir)
95
+ subdir = @copy[:subdir] if @copy.key?(:subdir)
76
96
  into = @copy[:into] if @copy.key?(:into)
77
97
  also = @copy[:also] if @copy.key?(:also)
78
98
  end
79
- return unless basedir
80
-
81
- items = [@project == @workspace.main ? nil : @workspace.home].concat(as_a(also))
99
+ items = [project == workspace.main ? nil : workspace.home].concat(as_a(also))
82
100
  items.each_with_index do |dir, i|
83
101
  if i == 0
84
102
  next unless dev? & !doc?
85
103
 
86
104
  dest = dir
87
105
  elsif dir.is_a?(::String)
88
- dest = @workspace.root_path(dir)
106
+ dest = workspace.root_path(dir)
89
107
  elsif dir.is_a?(::Symbol)
90
108
  dest = Repo.resolve(dir)&.path
91
109
  elsif dir.is_a?(Project)
@@ -99,8 +117,8 @@ module Squared
99
117
  from = val
100
118
  when :glob
101
119
  glob = val
102
- when :basedir
103
- basedir = val
120
+ when :subdir
121
+ subdir = val
104
122
  when :into
105
123
  into = val
106
124
  end
@@ -110,7 +128,7 @@ module Squared
110
128
  next unless dest&.directory?
111
129
 
112
130
  from = base_path(from)
113
- dest = dest.join(basedir, into || @project)
131
+ dest = dest.join(subdir, into || project)
114
132
  warn "cp #{from.join(glob)} #{dest}"
115
133
  copy_d(from, dest, glob: glob, verbose: verbose?)
116
134
  end
@@ -120,15 +138,30 @@ module Squared
120
138
  if @depend
121
139
  super
122
140
  else
141
+ frozen = flag == :frozen
123
142
  force = flag == :force
124
143
  dedupe = flag == :dedupe
125
144
  if (yarn = install_type(:yarn)) > 0
126
- cmd = session 'yarn', 'install'
145
+ cmd = session 'yarn'
127
146
  if yarn > 1
128
- cmd << '--check-cache' if force
147
+ if dedupe
148
+ cmd << 'dedupe'
149
+ else
150
+ cmd << 'install'
151
+ if force
152
+ cmd << '--check-cache'
153
+ elsif frozen
154
+ cmd << '--immutable'
155
+ end
156
+ end
129
157
  else
158
+ cmd << 'install'
159
+ if force
160
+ cmd << '--force'
161
+ elsif frozen
162
+ cmd << '--frozen-lockfile'
163
+ end
130
164
  cmd << '--production' if prod?
131
- cmd << '--force' if force
132
165
  cmd << '--ignore-engines' unless env('YARN_IGNORE_ENGINES', equals: '0')
133
166
  end
134
167
  elsif pnpm?
@@ -137,19 +170,29 @@ module Squared
137
170
  cmd << 'dedupe'
138
171
  else
139
172
  cmd << 'install'
140
- cmd << '--force' if force
173
+ if force
174
+ cmd << '--force'
175
+ elsif frozen
176
+ cmd << '--frozen-lockfile'
177
+ end
141
178
  end
142
179
  cmd << '--prod' if prod?
180
+ cmd << '--ignore-workspace' if env('NODE_WORKSPACES', equals: '0')
143
181
  append_nocolor
144
182
  else
145
- cmd = session 'npm'
146
- cmd << (dedupe ? 'dedupe' : 'install')
183
+ cmd = session 'npm', dedupe ? 'dedupe' : 'install'
184
+ if force
185
+ cmd << '--force'
186
+ elsif frozen
187
+ cmd << '--package-lock-only'
188
+ end
147
189
  cmd << '--omit=dev' if prod?
148
- cmd << '--force' if force
190
+ cmd << '--workspaces=false' if env('NODE_WORKSPACES', equals: '0')
191
+ cmd << '--package-lock=false' if env('NPM_PACKAGE_LOCK', equals: '0')
149
192
  append_nocolor
150
193
  end
151
194
  append_loglevel
152
- run(exception: @workspace.exception)
195
+ run(exception: workspace.exception)
153
196
  end
154
197
  end
155
198
 
@@ -159,23 +202,24 @@ module Squared
159
202
  cmd = pnpm? ? 'pnpm outdated' : 'npm outdated'
160
203
  print_item format_banner(message("#{cmd}#{opts.include?('dry-run') ? ' --dry-run' : ''}"), multitask: true)
161
204
  pwd = Dir.pwd
162
- Dir.chdir(@path)
205
+ Dir.chdir(path)
163
206
  info cmd
164
207
  data = `#{cmd} --json --loglevel=error`
165
208
  Dir.chdir(pwd)
166
- target = base_path('package.json')
167
- doc = File.read(target)
168
- json = JSON.parse(doc)
209
+ json = JSON.parse(doc = package.read)
210
+ pat = /^(\d+)(\.)(\d+)(\.)(\d+)$/
169
211
  dep1 = json['dependencies'] || {}
170
212
  dep2 = json['devDependencies'] || {}
171
213
  found = []
172
214
  avail = []
173
215
  if !data.empty?
174
- validate = ->(ver) { /^\d+\.\d+\.\d+$/.match?(ver) }
216
+ validate = ->(ver) { ver.match?(pat) }
175
217
  JSON.parse(data).each_pair do |key, val|
176
- file = dep1[key] || dep2[key]
218
+ val = val.find { |item| item['dependent'] == json['name'] } if val.is_a?(Array)
219
+ next unless val && (file = dep1[key] || dep2[key])
220
+
177
221
  ch = file[0]
178
- if !/[~^]/.match?(ch)
222
+ unless ch.match?(/[~^]/)
179
223
  avail << [key, file, val['latest'], true]
180
224
  next
181
225
  end
@@ -202,7 +246,14 @@ module Squared
202
246
  if upgrade
203
247
  next if file == want
204
248
 
205
- found << [key, file, want]
249
+ index = if a != c
250
+ 1
251
+ elsif b != d
252
+ 3
253
+ else
254
+ 5
255
+ end
256
+ found << [key, file, want, index]
206
257
  else
207
258
  avail << [key, file, val['latest'], false]
208
259
  end
@@ -223,13 +274,13 @@ module Squared
223
274
  else
224
275
  ['No packages were updated', 'possible']
225
276
  end
226
- puts Project.footer(empty_status(msg, hint, pending + val))
277
+ puts print_footer(empty_status(msg, hint, pending + val))
227
278
  end
228
279
  if !found.empty?
229
- col1 = size_col.(found, 0)
230
- col2 = size_col.(found, 1)
280
+ col1 = size_col.(found, 0) + 4
281
+ col2 = size_col.(found, 1) + 4
231
282
  found.each_with_index do |item, i|
232
- a, b, c = item
283
+ a, b, c, d = item
233
284
  cur = modified
234
285
  doc.sub!(/("#{Regexp.escape(a)}"\s*:\s*)"([~^])#{Regexp.escape(b)}"/) do |capture|
235
286
  if $2 == '~' && rev != :patch
@@ -241,29 +292,37 @@ module Squared
241
292
  "#{$1}\"#{$2 || ''}#{c}\""
242
293
  end
243
294
  end
244
- hint = if cur == -1
245
- 'SKIP'
246
- else
247
- modified > cur ? c : 'FAIL'
248
- end
249
- puts "#{pad_ord.(i, found)}. #{a.ljust(col1)}\t#{b.ljust(col2)}\t#{hint}"
295
+ a = a.ljust(col1)
296
+ c = if cur == -1
297
+ 'SKIP'
298
+ elsif modified == cur
299
+ 'FAIL'
300
+ elsif d == 1
301
+ a = sub_style(a, :bold)
302
+ sub_style(c, :green, :bold)
303
+ else
304
+ sub_style(c, :green, :bold, pat: pat, index: d)
305
+ end
306
+ puts "#{pad_ord.(i, found)}. #{a}#{b.ljust(col2)}#{c}"
250
307
  end
251
308
  pending = avail.reduce(pending) { |a, b| a + (b[3] ? 0 : 1) }
252
309
  if opts.include?('dry-run') || (modified == 0 && pending > 0)
253
310
  footer.(modified)
254
311
  elsif modified > 0
255
- File.write(target, doc)
312
+ File.write(package, doc)
256
313
  modified = -1
257
314
  footer.()
258
315
  commit(:add, 'package.json', pass: true)
259
316
  install if opts.include?('prune')
260
317
  end
261
318
  elsif !avail.empty?
262
- col1 = size_col.(avail, 0)
263
- col2 = size_col.(avail, 2)
319
+ col1 = size_col.(avail, 0) + 4
320
+ col2 = size_col.(avail, 1)
321
+ col3 = size_col.(avail, 2) + 4
264
322
  avail.each_with_index do |item, i|
265
323
  a, b, c, d = item
266
- puts "#{pad_ord.(i, avail)}. #{a.ljust(col1)}\t#{c.ljust(col2)}\t#{d ? 'locked at' : 'from'} #{b}"
324
+ e = sub_style(b.ljust(col2), d ? :red : :yellow)
325
+ puts "#{pad_ord.(i, avail)}. #{a.ljust(col1)}#{c.ljust(col3)}#{e} (#{d ? 'locked' : 'latest'})"
267
326
  pending += 1 unless d
268
327
  end
269
328
  footer.()
@@ -272,7 +331,12 @@ module Squared
272
331
  end
273
332
  end
274
333
 
275
- def compose(opts)
334
+ def run_script(cmd)
335
+ cmd.each { |val| run_s compose(val) }
336
+ end
337
+
338
+ def compose(args)
339
+ args ||= @output[1]
276
340
  cmd = [if yarn?
277
341
  'yarn'
278
342
  else
@@ -280,86 +344,93 @@ module Squared
280
344
  end]
281
345
  cmd << 'run'
282
346
  append_loglevel cmd
283
- if opts.is_a?(::Array)
284
- cmd += opts
347
+ if args.is_a?(::Array)
348
+ cmd += args
349
+ elsif args.is_a?(::String)
350
+ cmd << args
285
351
  else
286
- cmd << (opts || @workspace.script)
352
+ raise ArgumentError, message("#{cmd.first} script name", hint: args.nil? ? 'missing' : 'invalid')
287
353
  end
288
354
  cmd.join(' ')
289
355
  end
290
356
 
291
- def install_type(prog)
357
+ def install_type(prog = nil)
358
+ prog ||= yarn? ? :yarn : :pnpm
292
359
  key = :"#{prog}?"
293
- send(key) if respond_to?(key)
294
- @package[prog.to_sym] || 0
360
+ __send__(key) if respond_to?(key)
361
+ @pm[prog] || 0
362
+ end
363
+
364
+ def build?
365
+ @output[0] != false && (!@output[0].nil? || !@output[1].nil?)
295
366
  end
296
367
 
297
368
  def depend?
298
- !@depend.nil? || outdated?
369
+ !!@depend || outdated?
299
370
  end
300
371
 
301
372
  def copy?
302
- !@copy.nil?
373
+ !!@copy
303
374
  end
304
375
 
305
376
  def outdated?
306
- base_path('package.json').exist?
377
+ package.exist?
307
378
  end
308
379
 
309
380
  def yarn?
310
- (@package[:yarn] ||= if File.exist?(base_path('yarn.lock'))
311
- if File.exist?(rc = base_path('.yarnrc.yml'))
312
- begin
313
- require 'yaml'
314
- doc = YAML.load_file(rc)
315
- doc.nodeLinker == 'node-modules' ? 2 : 3
316
- rescue StandardError
317
- 3
318
- end
319
- else
320
- 1
321
- end
322
- else
323
- ver = env('NODE_INSTALL')
324
- if ver&.start_with?('yarn')
325
- ver.include?('@1') ? 1 : 3
326
- else
327
- 0
328
- end
329
- end) > 0
381
+ (@pm[:yarn] ||= if File.exist?(base_path('yarn.lock'))
382
+ if File.exist?(rc = base_path('.yarnrc.yml'))
383
+ begin
384
+ require 'yaml'
385
+ doc = YAML.load_file(rc)
386
+ doc.nodeLinker == 'node-modules' ? 2 : 3
387
+ rescue StandardError
388
+ 3
389
+ end
390
+ else
391
+ 1
392
+ end
393
+ else
394
+ ver = env('NODE_INSTALL')
395
+ if ver&.start_with?('yarn')
396
+ ver.include?('@1') ? 1 : 3
397
+ else
398
+ 0
399
+ end
400
+ end) > 0
330
401
  end
331
402
 
332
403
  def pnpm?
333
- (@package[:pnpm] ||= if File.exist?(base_path('pnpm-lock.yaml'))
334
- begin
335
- require 'yaml'
336
- doc = YAML.load_file(base_path('node_modules/.modules.yaml'))
337
- case doc['nodeLinker']
338
- when 'hoisted'
339
- 1
340
- when 'pnp'
341
- 3
342
- else
343
- 4
344
- end
345
- rescue StandardError
346
- 4
347
- end
348
- else
349
- env('NODE_INSTALL')&.start_with?('pnpm') ? 4 : 0
350
- end) > 0
404
+ (@pm[:pnpm] ||= if File.exist?(base_path('pnpm-lock.yaml'))
405
+ begin
406
+ require 'yaml'
407
+ doc = YAML.load_file(base_path('node_modules/.modules.yaml'))
408
+ case doc['nodeLinker']
409
+ when 'hoisted'
410
+ 1
411
+ when 'pnp'
412
+ 3
413
+ else
414
+ 4
415
+ end
416
+ rescue StandardError
417
+ 4
418
+ end
419
+ else
420
+ env('NODE_INSTALL')&.start_with?('pnpm') ? 4 : 0
421
+ end) > 0
351
422
  end
352
423
 
353
424
  def dev?
354
425
  return false if Node.prod?
355
426
 
356
- @workspace.dev?(@dev, script: @output.last)
427
+ workspace.dev?(script: @output[1], pat: @dev, global: !@script || @script[:run].nil?)
357
428
  end
358
429
 
359
430
  def prod?
360
431
  return true if Node.prod?
361
432
 
362
- @workspace.prod?(@prod, script: @output.last)
433
+ workspace.prod?(script: @output[1], pat: @prod, global: !@script || @script[:run].nil?)
363
434
  end
364
435
 
365
436
  protected
@@ -367,27 +438,39 @@ module Squared
367
438
  def append_loglevel(cmd = @session)
368
439
  return unless (level = env('NODE_LOGLEVEL'))
369
440
 
441
+ silent = !workspace.verbose || level == 'silent'
370
442
  if yarn?
371
443
  if install_type(:yarn) == 1
372
- case level
373
- when 'silent', 'verbose'
374
- cmd << "--#{level}"
444
+ if silent
445
+ cmd << '--silent'
446
+ elsif level == 'verbose'
447
+ cmd << '--verbose'
375
448
  end
376
449
  end
377
450
  elsif pnpm?
451
+ if silent
452
+ cmd << '--reporter=silent'
453
+ level ||= 'error'
454
+ end
378
455
  case level
379
456
  when 'debug', 'info', 'warn', 'error'
380
457
  cmd << "--loglevel=#{level}"
381
- when 'silent'
382
- cmd << '--reporter=silent'
383
458
  end
459
+ elsif silent
460
+ cmd << '--loglevel=silent'
384
461
  else
385
462
  case level
386
- when 'silent', 'error', 'warn', 'notice', 'http', 'info', 'verbose', 'silly'
463
+ when 'error', 'warn', 'notice', 'http', 'info', 'verbose', 'silly'
387
464
  cmd << "--loglevel=#{level}"
388
465
  end
389
466
  end
390
467
  end
468
+
469
+ private
470
+
471
+ def package
472
+ @package ||= base_path('package.json')
473
+ end
391
474
  end
392
475
  end
393
476
  end
@@ -4,31 +4,37 @@ module Squared
4
4
  module Repo
5
5
  module Project
6
6
  class Python < Git
7
+ REF = :python
8
+ REQUIREMENTS = %w[requirements.txt pyproject.toml setup.py].freeze
9
+ private_constant :REF, :REQUIREMENTS
10
+
7
11
  class << self
12
+ def populate(*); end
13
+
8
14
  def tasks
9
15
  nil
10
16
  end
11
17
 
12
- def to_sym
13
- :python
14
- end
15
-
16
18
  def venv?
17
- path = ENV['VIRTUAL_ENV']
18
- !path.nil? && Dir.exist?(path)
19
+ val = ENV['VIRTUAL_ENV']
20
+ !val.nil? && Dir.exist?(val)
19
21
  end
20
22
 
21
- def is_a?(path)
22
- if path.is_a?(::String) || path.is_a?(::Pathname)
23
- base = Pathname.new(path)
24
- ['setup.py', 'requirements.txt'].any? { |file| base.join(file).exist? }
23
+ def is_a?(val)
24
+ if (val = as_path(val))
25
+ REQUIREMENTS.any? { |file| val.join(file).exist? }
25
26
  else
26
27
  super
27
28
  end
28
29
  end
29
30
  end
30
31
 
31
- @@tasks[:python] = {
32
+ def initialize(name, path, workspace, *, **kwargs)
33
+ super
34
+ initialize_build(REF, **kwargs)
35
+ end
36
+
37
+ @@tasks[REF] = {
32
38
  install: %i[upgrade force]
33
39
  }.freeze
34
40
 
@@ -36,8 +42,8 @@ module Squared
36
42
  super
37
43
  return unless outdated?
38
44
 
39
- namespace @name do
40
- @@tasks[:python].each do |action, flags|
45
+ namespace name do
46
+ @@tasks[REF].each do |action, flags|
41
47
  namespace action do
42
48
  flags.each do |flag|
43
49
  case action
@@ -65,32 +71,45 @@ module Squared
65
71
  def depend(flag = nil, opts: [])
66
72
  if @depend
67
73
  super
68
- else
69
- cmd = pip_session 'install -r requirements.txt'
70
- case flag
71
- when :upgrade
72
- cmd << '--upgrade'
73
- cmd << '--upgrade-strategy=eager' if opts.include?('eager')
74
- when :force
75
- cmd << '--force-reinstall'
74
+ elsif outdated?
75
+ case install_type
76
+ when 1, 2
77
+ cmd = pip_session 'install'
78
+ case flag
79
+ when :upgrade
80
+ cmd << '--upgrade'
81
+ cmd << '--upgrade-strategy=eager' if opts.include?('eager')
82
+ when :force
83
+ cmd << '--force-reinstall'
84
+ end
85
+ cmd << '--pre' if opts.include?('pre')
86
+ cmd << '--require-virtualenv' if opts.include?('venv')
87
+ cmd << '--no-cache-dir' if opts.include?('no-cache')
88
+ cmd << '--dry-run' if opts.include?('dry-run')
89
+ append_nocolor
90
+ cmd << (install_type == 1 ? '-r requirements.txt' : '.')
91
+ run(exception: workspace.exception)
92
+ when 3
93
+ run_s "#{@bin} setup.py install"
76
94
  end
77
- cmd << '--pre' if opts.include?('pre')
78
- cmd << '--require-virtualenv' if opts.include?('venv')
79
- cmd << '--no-cache-dir' if opts.include?('no-cache')
80
- cmd << '--dry-run' if opts.include?('dry-run')
81
- append_nocolor
82
- run(exception: @workspace.exception)
83
95
  end
84
96
  end
85
97
 
86
98
  def outdated(*); end
87
99
 
100
+ def install_type(*)
101
+ return @requirements if @requirements
102
+
103
+ ret = REQUIREMENTS.index { |file| base_path(file).exist? }
104
+ @requirements = ret ? ret + 1 : 0
105
+ end
106
+
88
107
  def depend?
89
- !@depend.nil? || outdated?
108
+ !!@depend || outdated?
90
109
  end
91
110
 
92
111
  def outdated?
93
- base_path('requirements.txt').exist?
112
+ install_type > 0
94
113
  end
95
114
 
96
115
  private