sewing_kit 0.7.1 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
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