sewing_kit 0.7.1 → 0.8.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: a7cc8acdd9a3ae14f572f3b9f403e8152e805931
4
- data.tar.gz: f836bb3b228af0f0f98d243fb7665a52bb4568a3
3
+ metadata.gz: c158fee225e3c96470ca32e8af145fc261e9de9d
4
+ data.tar.gz: 41b8baaa9f319ce1bc985383f7e2a1fe6168fb3e
5
5
  SHA512:
6
- metadata.gz: a3ea45f34e1d688d50d4e14e64bed8c7fa8f0d678b5d615c1efd38948be1ab38247c9a0a797740e09621dd1dbe522372838ecf2cbd50ebdb491e1a42370818f2
7
- data.tar.gz: 7ed221b39eeeb80d1dcc43d7742983776d0d5d5297050bf756c8ddc8e8d67b79fee39e72c0d8b1e2d55f00412531d66f1c3d7ad643319725cf0636cacd25fb54
6
+ metadata.gz: c3b3adcaa26e1185fd5515387d0eb2c1c965a79f432b350601569a1a9f634175c3a703fa5ba89d151e7e185177eb8d4afc124f50cf43420391a55fcc1b4d047d
7
+ data.tar.gz: 35d946b5848e8353213ab8d81f7627524e435e5220d70f04f70065808f78a62dd6a948e9a28d672e683120812ffaa1b48d310cc41ce21f48c1b59a49b69d10bb
data/README.md CHANGED
@@ -1,9 +1,89 @@
1
1
  # sewing_kit
2
2
 
3
- A solid foundation for modern FED stacks in Shopify apps.
3
+ Zero configuration, high performance front end development at organization scale.
4
4
 
5
- ## Other repos
5
+ This document focuses on Rails integration. For details of `sewing-kit`'s configuration and usage, see the [sewing-kit documentation](https://github.com/Shopify/sewing-kit#sewing-kit).
6
6
 
7
- https://github.com/rails/webpacker
8
- https://github.com/shakacode/react_on_rails
9
- https://github.com/mipearson/webpack-rails
7
+ ## Quick Start
8
+ Create a Rails project using `dev init` then:
9
+
10
+ ### Install Sewing Kits
11
+ ```sh
12
+ # Add Ruby/Node dependencies
13
+ bundle add sewing_kit
14
+ yarn add --dev @shopify/sewing-kit
15
+ yarn add --dev react-hot-loader@3.0.0-beta.7
16
+
17
+ # Optional - add Polaris
18
+ yarn add @shopify/polaris react react-dom
19
+
20
+ yarn
21
+ dev up
22
+ ```
23
+
24
+ ### Add JavaScript
25
+ sewing_kit looks for JavaScript in `app/ui/index.js`. The code in `index.js` (and any imported JS/CSS) will be built into a `main` bundle.
26
+
27
+ ### Link to JS/CSS with `erb` Helpers
28
+ The `main` bundle is imported into `erb` files using Rails helpers:
29
+
30
+ ```erb
31
+ <%= sewing_kit_link_tag *sewing_kit_asset_paths('main') %>
32
+ <%= sewing_kit_script_tag *sewing_kit_asset_paths('main') %>
33
+ ```
34
+
35
+ **Note:** CSS `<link>` tags appear only in production; in development, CSS is embedded within the `main.js` bundle.
36
+
37
+ ## Minimal Project Layout
38
+ ```
39
+ ├── Gemfile (must contain "gem 'sewing_kit")
40
+ ├── package.json (must specify '@shopify/sewing-kit' as a 'devDependency')
41
+
42
+ └── app
43
+ └── ui
44
+ │ └─- index.js
45
+ └── views
46
+ └─- layouts
47
+ └─- application.html.erb (must link to JS / CSS using sewing_kit_script_tag / sewing_kit_link_tag
48
+ ```
49
+
50
+ ## Rails, Polaris, and React Layout
51
+ A typical Polaris app will use React to render views and components. The following layout shows best practice locations for:
52
+ - Global SCSS settings
53
+ - App sections (roughly analogous to Rails routes)
54
+ - Components
55
+ - Co-located CSS modules
56
+ - Co-located unit tests
57
+
58
+ ```
59
+ └── app
60
+ └── ui
61
+ ├─- index.js (renders React into the DOM)
62
+ ├── styles (optional)
63
+ │ └── settings.scss (global vars and @polaris overrides)
64
+
65
+ └── components (optional)
66
+ ├── App
67
+ │ ├── index.js
68
+ │ ├── App.js
69
+ │ └── tests
70
+ │ └── App.test.js
71
+
72
+ ├-─ MyComponent
73
+ │ ├-─ index.js
74
+ │ ├-─ MyComponent.js
75
+ │ ├── MyComponent.scss (optional; component-scoped CSS styles, mixins, etc)
76
+ │ └── tests
77
+ │ └── MyComponent.test.js
78
+
79
+ └── sections (optional; container views that compose presentation components into UI blocks)
80
+ └── Home
81
+ ├-─ index.js
82
+ └── Home.js
83
+ ```
84
+
85
+ ## React Boilerplate
86
+ * Create a React app in `app/ui/App.js` ([example](https://github.com/Shopify/rails_sewing_kit_example/blob/master/app/ui/App.js#L11))
87
+ * In an `erb` view, add a placeholder for React content ([example](https://github.com/Shopify/rails_sewing_kit_example/blob/master/app/views/home/index.html.erb#L4))
88
+ * In `index.js`, render a React component into the placeholder element ([example](https://github.com/Shopify/rails_sewing_kit_example/blob/master/app/ui/index.js))
89
+ * Use `sewing_kit_script_tag`/`sewing_kit_link_tag` helpers to link erb/js ([example](https://github.com/Shopify/rails_sewing_kit_example/blob/master/app/views/layouts/application.html.erb#L8))
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module SewingKit
2
3
  end
3
4
 
@@ -1 +1,2 @@
1
+ # frozen_string_literal: true
1
2
  require "sewing_kit/railtie" if defined? Rails
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require 'rails'
2
3
  require 'rails/railtie'
3
4
  require 'sewing_kit/webpack/compiler'
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module SewingKit
2
- VERSION = "0.7.1"
3
+ VERSION = "0.8.0"
3
4
  end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module SewingKit
2
3
  module Webpack
3
4
  class Compiler
@@ -7,11 +8,11 @@ module SewingKit
7
8
  *command,
8
9
  chdir: Rails.root.to_s,
9
10
  out: $stdout,
10
- err: $stderr,
11
+ err: $stderr
11
12
  )
12
13
 
13
- if !result
14
- puts "sewing-kit compile failed with error code #{$?}"
14
+ unless result
15
+ puts "sewing-kit compile failed with error code #{$CHILD_STATUS}"
15
16
  exit(1)
16
17
  end
17
18
  result
@@ -24,11 +25,10 @@ module SewingKit
24
25
  'node_modules/.bin/sewing-kit',
25
26
  'build',
26
27
  '--mode',
27
- node_env,
28
+ node_env
28
29
  ].reject(&:empty?)
29
30
  end
30
31
 
31
- private
32
32
  def node_env
33
33
  ENV['NODE_ENV'] || Rails.env.to_s
34
34
  end
@@ -1,16 +1,16 @@
1
+ # frozen_string_literal: true
1
2
  module SewingKit
2
3
  module Webpack
3
- class NodeSewingKitNotInstalled < StandardError
4
- def initialize()
5
- super(
6
- "sewing-kit is not available. " +
7
- "Please add `@shopify/sewing-kit` to package.json's `devDependencies`, " +
8
- "and run `yarn install`."
9
- )
4
+ class Dev
5
+ class NodeSewingKitNotInstalled < StandardError
6
+ def initialize
7
+ super(
8
+ "sewing-kit is not installed. " \
9
+ "Try `yarn add --dev @shopify/sewing-kit`"
10
+ )
11
+ end
10
12
  end
11
- end
12
13
 
13
- class Dev
14
14
  attr_accessor :pid
15
15
 
16
16
  def start
@@ -22,7 +22,7 @@ module SewingKit
22
22
 
23
23
  def spawn
24
24
  sewing_kit_bin = 'node_modules/.bin/sewing-kit'
25
- raise NodeSewingKitNotInstalled.new unless File.exist?(sewing_kit_bin)
25
+ raise NodeSewingKitNotInstalled unless File.exist?(sewing_kit_bin)
26
26
 
27
27
  Kernel.spawn(
28
28
  {
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require 'base64'
2
3
  require 'action_view/helpers'
3
4
  require 'sewing_kit/webpack/manifest'
@@ -10,26 +11,24 @@ module SewingKit
10
11
  class UnknownJavaScriptAssetError < StandardError
11
12
  end
12
13
 
13
- def sewing_kit_asset_paths(entrypointName, extension: 'js')
14
- return '' unless entrypointName.present?
14
+ def sewing_kit_asset_paths(entrypoint_name, extension: 'js')
15
+ return '' unless entrypoint_name.present?
15
16
 
16
- paths = SewingKit::Webpack::Manifest.asset_paths(entrypointName)
17
+ paths = SewingKit::Webpack::Manifest.asset_paths(entrypoint_name)
17
18
  return '' unless paths && paths[extension]
18
19
 
19
- entryPaths = paths[extension]
20
- if serve_development_assets? && extension == 'js'
21
- entryPaths.unshift('/webpack/assets/dll/vendor.js')
22
- end
23
- entryPaths
20
+ entry_paths = paths[extension]
21
+ entry_paths.unshift('/webpack/assets/dll/vendor.js') if serve_development_assets? && extension == 'js'
22
+ entry_paths
24
23
  end
25
24
 
26
25
  def sewing_kit_link_tag(*paths)
27
26
  options = paths.extract_options!
28
27
 
29
- tags = paths.uniq.map { |path|
28
+ tags = paths.uniq.map do |path|
30
29
  next '' if path == ''
31
30
  create_asset_tag(:link, path, options)
32
- }
31
+ end
33
32
 
34
33
  safe_join(tags, "\n")
35
34
  end
@@ -37,11 +36,11 @@ module SewingKit
37
36
  def sewing_kit_script_tag(*paths)
38
37
  options = paths.extract_options!
39
38
 
40
- tags = paths.uniq.map { |path|
39
+ tags = paths.uniq.map do |path|
41
40
  next '' if path == ''
42
41
 
43
42
  create_asset_tag(:script, path, options)
44
- }
43
+ end
45
44
 
46
45
  safe_join(tags, "\n")
47
46
  end
@@ -55,7 +54,7 @@ module SewingKit
55
54
 
56
55
  if tag_options[:integrity]
57
56
  file_hash = extract_hash(path)
58
- options[:integrity] = file_hash ? encode_hash(file_hash) : nil
57
+ options[:integrity] = maybe_integrity(file_hash)
59
58
  end
60
59
 
61
60
  case tag_type
@@ -66,6 +65,11 @@ module SewingKit
66
65
  end
67
66
  end
68
67
 
68
+ def maybe_integrity(file_hash)
69
+ return nil unless file_hash
70
+ encode_hash(file_hash)
71
+ end
72
+
69
73
  def encode_hash(hash)
70
74
  # sewing-kit's default config places a sha256 hash in the filename. Browsers expect integrity
71
75
  # to be the Base64 encoded version of the binary hash prefixed with the hashing algorithm
@@ -1,13 +1,41 @@
1
+ # frozen_string_literal: true
2
+ require 'open3'
1
3
  require 'uri'
2
4
 
3
5
  module SewingKit
4
6
  module Webpack
5
7
  # Webpack manifest loading, caching & entry point retrieval
6
8
  class Manifest
7
- # Raised if we can't read our webpack manifest for whatever reason
9
+ # Raised if the node sewing-kit isn't installed/runnable.
10
+ class NodeSewingKitNotRunnable < StandardError
11
+ def initialize(mode, cause)
12
+ env_message = if 'production' == mode
13
+ "\nIf this is a container build, try:\n" \
14
+ " - Adding `YARN_PRODUCTION=false` to your pipeline's environment variables\n" \
15
+ " - Adding the node buildpack: https://github.com/heroku/heroku-buildpack-nodejs"
16
+ else
17
+ "Try `yarn add --dev @shopify/sewing-kit`"
18
+ end
19
+
20
+ super(
21
+ "Could not fetch manifest because node sewing-kit is not runnable. " \
22
+ "#{env_message}\n" \
23
+ "Original error #{cause}"
24
+ )
25
+ end
26
+ end
27
+
28
+ # Raised if the node-generated manifest cannot be read from the filesystem.
8
29
  class ManifestLoadError < StandardError
9
- def initialize(message, orig)
10
- super "#{message} (original error #{orig})"
30
+ def initialize(path, cause)
31
+ super "Could not load manifest from #{path} (original error #{cause})"
32
+ end
33
+ end
34
+
35
+ # Raised if the node-generated manifest returns unparseable JSON.
36
+ class ManifestParseError < StandardError
37
+ def initialize(cause)
38
+ super "Could not parse manifest JSON (original error #{cause})"
11
39
  end
12
40
  end
13
41
 
@@ -19,55 +47,30 @@ module SewingKit
19
47
 
20
48
  class << self
21
49
  # :nodoc:
22
- def asset_paths(entrypointName)
23
- metadata['entrypoints'][entrypointName]
50
+ def asset_paths(entrypoint_name)
51
+ instance.asset_paths(entrypoint_name)
24
52
  end
25
53
 
26
54
  def clear_cache!
27
- @metadata = nil
28
- @metadata_expiry_time = nil
55
+ @instance = nil
29
56
  end
30
57
 
31
58
  def manifest
32
- metadata['assets']
59
+ instance.manifest
33
60
  end
34
61
 
35
- private
36
-
37
- def metadata
38
- if ::Rails.env.production?
39
- # Cache at class level, as JSON loading/parsing can be expensive.
40
- @metadata ||= load_metadata
62
+ def instance
63
+ return @instance if @instance
64
+ @instance = if Rails.env.development? && ENV['SK_SIMULATE_PRODUCTION'] != '1'
65
+ Development.new
41
66
  else
42
- # In development, the manifest may change.
43
- # A short cache lifetime avoids time consuming node callouts.
44
- if self.instance_variable_defined?('@metadata_expiry_time') && @metadata_expiry_time && @metadata_expiry_time >= Time.now
45
- @metadata
46
- else
47
- @metadata = load_metadata
48
- @metadata_expiry_time = Time.now + 4
49
- end
67
+ Production.new
50
68
  end
51
-
52
- raise LegacyManifestError.new(@metadata) if !@metadata['entrypoints']
53
-
54
- @metadata
55
- end
56
-
57
- def manifest_bundled?
58
- !manifest["errors"].any? { |error| error.include? "Module build failed" }
59
- end
60
-
61
- def load_metadata
62
- JSON.parse(`node_modules/.bin/sewing-kit manifest --mode #{mode}`)
63
- rescue => e
64
- raise ManifestLoadError.new("Could not load compiled manifest - have you run `rake sewing_kit:build`?", e)
65
- end
66
-
67
- def mode
68
- ENV['NODE_ENV'] || Rails.env.to_s || 'production'
69
69
  end
70
70
  end
71
71
  end
72
72
  end
73
73
  end
74
+
75
+ require 'sewing_kit/webpack/manifest/development'
76
+ require 'sewing_kit/webpack/manifest/production'
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+ module SewingKit
3
+ module Webpack
4
+ class Manifest
5
+ class Base
6
+ def initialize
7
+ @metadata = nil
8
+ end
9
+
10
+ # :nodoc:
11
+ def asset_paths(entrypoint_name)
12
+ metadata['entrypoints'][entrypoint_name]
13
+ end
14
+
15
+ def clear_cache!
16
+ @metadata = nil
17
+ end
18
+
19
+ def manifest
20
+ metadata['assets']
21
+ end
22
+
23
+ def load_metadata_from_node
24
+ begin
25
+ stdout, * = Open3.capture3('node_modules/.bin/sewing-kit', 'manifest', "-- mode #{mode}")
26
+ rescue => e
27
+ raise NodeSewingKitNotRunnable.new(mode, e)
28
+ end
29
+
30
+ begin
31
+ JSON.parse(stdout)
32
+ rescue => e
33
+ raise ManifestParseError, e
34
+ end
35
+ end
36
+
37
+ private
38
+
39
+ def mode
40
+ Rails.env.to_s || 'production'
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+ require 'sewing_kit/webpack/manifest/base'
3
+
4
+ module SewingKit
5
+ module Webpack
6
+ class Manifest
7
+ class Development < Base
8
+ def initialize
9
+ super
10
+ @metadata_path = nil
11
+ end
12
+
13
+ def clear_cache!
14
+ super
15
+ @metadata_path = nil
16
+ end
17
+
18
+ def metadata
19
+ load_metadata
20
+ end
21
+
22
+ def load_metadata
23
+ begin
24
+ return load_metadata_from_fs(@metadata_path) if @metadata_path
25
+ rescue => e
26
+ Rails.logger.warn "[sewing_kit] could not read manifest from #{@metadata_path}; falling back to node. #{e}"
27
+ @metadata_path = nil
28
+ end
29
+
30
+ result = load_metadata_from_node
31
+ raise LegacyManifestError, result unless result['entrypoints']
32
+
33
+ @metadata_path = result && result['path']
34
+ result
35
+ end
36
+
37
+ def load_metadata_from_fs(path)
38
+ begin
39
+ json_str = File.read(path)
40
+ rescue => e
41
+ raise ManifestLoadError.new(path, e)
42
+ end
43
+
44
+ begin
45
+ JSON.parse(json_str)
46
+ rescue => e
47
+ raise ManifestParseError, e
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+ require 'sewing_kit/webpack/manifest/base'
3
+
4
+ module SewingKit
5
+ module Webpack
6
+ class Manifest
7
+ class Production < Base
8
+ @metadata = nil
9
+ @metadata_path = nil
10
+
11
+ # :nodoc:
12
+ def asset_paths(entrypoint_name)
13
+ metadata['entrypoints'][entrypoint_name]
14
+ end
15
+
16
+ def metadata
17
+ # Cache at class level, as JSON loading/parsing can be expensive.
18
+ @metadata ||= load_metadata
19
+ end
20
+
21
+ def load_metadata
22
+ result = load_metadata_from_node
23
+ raise LegacyManifestError, result unless result['entrypoints']
24
+ result
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  TEMPLATE_PATH = File.expand_path("../install/template.rb", File.dirname(__FILE__))
2
3
 
3
4
  namespace :sewing_kit do
@@ -7,7 +8,7 @@ namespace :sewing_kit do
7
8
  end
8
9
 
9
10
  desc "Build webpack asset bundles"
10
- task :build => :environment do
11
+ task build: :environment do
11
12
  compiler = SewingKit::Webpack::Compiler.new
12
13
  compiler.compile
13
14
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sewing_kit
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.1
4
+ version: 0.8.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Chris Sauve
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-11-09 00:00:00.000000000 Z
11
+ date: 2018-01-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: railties
@@ -38,6 +38,34 @@ dependencies:
38
38
  - - ">="
39
39
  - !ruby/object:Gem::Version
40
40
  version: 3.2.0
41
+ - !ruby/object:Gem::Dependency
42
+ name: rubocop
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '0.51'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '0.51'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rubocop-git
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: 0.1.3
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: 0.1.3
41
69
  description:
42
70
  email:
43
71
  - chris.sauve@shopify.com
@@ -54,6 +82,9 @@ files:
54
82
  - lib/sewing_kit/webpack/dev.rb
55
83
  - lib/sewing_kit/webpack/helper.rb
56
84
  - lib/sewing_kit/webpack/manifest.rb
85
+ - lib/sewing_kit/webpack/manifest/base.rb
86
+ - lib/sewing_kit/webpack/manifest/development.rb
87
+ - lib/sewing_kit/webpack/manifest/production.rb
57
88
  - lib/tasks/sewing_kit.rake
58
89
  homepage:
59
90
  licenses:
@@ -75,7 +106,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
75
106
  version: '0'
76
107
  requirements: []
77
108
  rubyforge_project:
78
- rubygems_version: 2.5.2.1
109
+ rubygems_version: 2.6.14
79
110
  signing_key:
80
111
  specification_version: 4
81
112
  summary: Shopify’s modern front-end tooling, nicely packaged for Rails