Last active
December 14, 2015 22:59
-
-
Save davegurnell/5162070 to your computer and use it in GitHub Desktop.
Rough idea for an update to the Untyped sbt-js and sbt-less plugins: create a single asset compiler plugin that allows flexible creation of compiler functions.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// TODO: This code doesn't distinguish between "main" files for which we want to generate outputs, | |
// and "library" files that are used in compilation but don't generate outputs. | |
case class SourceLocation(val rel: File, val relTo: File, val abs: File) | |
// Compilation may take several steps (Coffeescript => CommonJS => Concat => Uglifyjs etc). | |
// Sources are cached on disk after each step, allowing us to check timestamps to avoid | |
// unnecessary recompilation. Compiler steps accept one or more Sources as an argument and | |
// emit one or more Sources as outputs. Each Source knows where the original source file is | |
// located, where the target file is going to end up, and where the content is cached at the | |
// moment. | |
case class Source( | |
val name: String, // The name used to require this file from another | |
val src: SourceLocation, // Where did this file originally come from ? | |
val des: SourceLocation, // Where is this file going to end up ? | |
val loc: SourceLocation, // Where is the current cached version of this file | |
val deps: List[SourceLocation] // Ordering dependencies. Calculated by a resolver and used in a CompileManyOne | |
) | |
// Basic types: | |
type SearchPath = List[File] // A "classpath" for resolving dependencies, similar to current sbt-{js,less} functionality | |
type Sources = List[Source] // A set of sources output by a compilation step | |
// Resolvers find sources of a given type given a search path. | |
// They extract the name and dependencies from the file and produce a Source object. | |
type SourceResolver = SearchPath => Sources | |
// Resolvers for different source types: | |
// Arguments are things like path finders and include and exclude filters. | |
case class ResolveCoffee(...) extends SourceResolver | |
case class ResolveJs(...) extends SourceResolver | |
// The grand goal of a compiler is to turn a search path into a set of sources: | |
type Compiler = SearchPath => Sources | |
// Generic compilation steps: | |
type CompileOneOne = Source => Source | |
type CompileManyOne = List[Source] => Source | |
type CompileManyMany = List[Source] => List[Source] | |
// Turn a one-one compilation step into a many-many (should probably be using Scalaz): | |
implicit def liftOneOne(fn: CompileOneOne): CompileManyMany = { | |
(in: Sources) => | |
in map fn | |
} | |
def compileAll(fns: Compiler *): Compiler = { | |
(in: SearchPath) => | |
fns.toList.flatMap(_ apply in) | |
} | |
// One-to-one compilation steps: | |
case class Coffee(val bare: Boolean) extends CompileOneOne { ... } | |
case object CommonJs extends CompileOneOne { ... } | |
case object UglifyJs extends CompileOneOne { ... } | |
// Many-to-one compilation steps: | |
object Concat extends CompileManyOne { ... } | |
object GoogleClosure(val options: CompilerOptions) extends CompileManyOne { ... } | |
// These steps take a search path, resolve all relevant files, and compile them into temporary JS files: | |
val coffeeCompiler: Compiler = ResolveCoffee(...) andThen Coffee(false) andThen CommonJs | |
val jsCompiler: Compiler = ResolveJs(...) andThen CommonJs | |
// The whole compiler: | |
val compile = compileAll(coffeeCompiler, jsCompiler) andThen Concat andThen UglifyJs | |
val outputs = compile(searchPath) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment