Skip to content

Instantly share code, notes, and snippets.

@foca
Created July 6, 2018 23:26
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save foca/c720318018f334cbe9dc7f3818d272eb to your computer and use it in GitHub Desktop.
Save foca/c720318018f334cbe9dc7f3818d272eb to your computer and use it in GitHub Desktop.
Tiny script to build GNU Make dependency files for JS `import` rules or CSS `@import` rules.
#!/usr/bin/env ruby
# Generate make dependencies by parsing a tree of JS / CSS files.
#
# Usage
# -----
#
# Add this to your Makefile:
#
# ```
# .deps.js.mk:
# makedepend --js ./assets/js > $@
#
# .deps.css.mk:
# makedepend --css ./assets/css > $@
#
# -include .deps.js.mk .deps.css.mk
# ```
#
# How it works
# ------------
#
# ```
# // foo.js
# import Bar from './bar.js'
# import './baz.js'
# import _ from 'lodash'
# <code>
#
# // bar.js
# import './baz.js'
# <code>
#
# // baz.js
# <code, but no imports>
# ```
#
# It will generate the following output:
#
# ```
# foo.js: bar.js baz.js
# @touch $@
#
# bar.js: baz.js
# @touch $@
# ```
#
# You can see `baz` is not set as a rule -- only a dependency, since it doesn't
# depend on anything itself. Finally, `lodash` doesn't resolve to a file in our
# tree (it's most likely an npm dependency), so there's no need to have a rule
# for that.
require "pathname"
require "optparse"
Format = Struct.new(:desc, :glob, :scanner, :extractor)
types = {
js: Format.new(
"Scan for ES2016 `import` statements",
"**/*.js", /^\s*import/, /^.*["'](.+)["'].*$/
),
css: Format.new(
"Scan for CSS `@import` statements",
"**/*.css", /^\s*@import/, /^.*["'](.+)["'].*$/
)
}
type = nil
parser = OptionParser.new do |opts|
opts.banner = "Usage: #{$0} [--js|--css] <directory>"
types.each do |key, format|
opts.on("--#{key}", format.desc) do |set|
type = format if set
end
end
end
parser.parse!
puts Pathname.new(ARGV.fetch(0) { abort parser.banner })
.glob(type.glob)
.each_with_object({}) { |file, deps|
deps[file] = file.each_line
.grep(type.scanner)
.map { |import| file.dirname + import.sub(type.extractor, '\1').chomp }
.select { |import| import.file? }
}
.select { |_, deps| deps.any? }
.map { |file, deps| format("%s: %s\n\t@touch $@", file, deps.join(" ")) }
.join("\n\n")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment