caffeine-mc
Version:
Select, configure and extend your to-JavaScript compiler, with arbitrary code, on a per file bases from within the file.
146 lines (120 loc) • 4.86 kB
text/coffeescript
{
array, merge, formattedInspect, log, defineModule, isString, upperCamelCase, randomBase62Character
consistentJsonStringify
Promise
currentSecond
} = require 'art-standard-lib'
{BaseClass} = require 'art-class-system'
{findSourceRootSync} = require './SourceRoots'
{findModuleSync} = require './ModuleResolver'
require 'colors'
fs = require 'fs-extra'
glob = require 'glob-promise'
crypto = require 'crypto'
os = require 'os'
path = require 'path'
###
cachedFileKey: (object)
compiler: (required, object) compiler
source: (required, string) source-code
sourceFile: (required, string) source file path & name
compilerOptions: (object) all options which affect the generated output
cachedFileKeyWithCompilerResults: (object)
all the cachedFileKey fields
compiled: (required, object) the compiler's results
###
defineModule module, class CompileCache extends BaseClass
: "CaffineMcCompileCache"
compileCachePathRoot: -> os.tmpdir()
compileCacheFilePathRoot: ->
||= path.join ,
: (compiler) ->
isString(compiler.version) && compiler
: (compiler) -> compiler.getName?() || compiler.name
: (compiler) ->
"#{@getCompilerName compiler}-#{compiler.version}"
: (sha256String) -> sha256String.replace /[\/+=]/g, "-"
: (source) ->
(
crypto.createHmac 'sha256', "no need for a real secret"
.update source
.digest 'base64'
.split("=")[0]
)
###
IN: cachedFileKey (see above)
###
: (cachedFileKey) ->
{compiler, source, sourceFile, compilerOptions} = cachedFileKey
unless compiler && sourceFile && source?
throw new Error "expecting compiler, source and sourceFile: " + formattedInspect {compiler, source, sourceFile}
return null unless compiler
sourceRoot = findSourceRootSync sourceFile
relativeSourceFile = path.relative sourceRoot, sourceFile
source = """
# sourceFile: #{relativeSourceFile}
# compilerOptions: #{consistentJsonStringify compilerOptions ? null}
#{source}
"""
[
path.basename(sourceRoot).replace /\./g, "-"
path.basename(sourceFile).replace /\./g, "-"
compiler
source
].join("_") + ".json"
###
IN: cachedFileKey (see above), but also with {source, compiled and props}
###
: (cachedFileKey) ->
if fileName = cachedFileKey
{source, compiled, props} = cachedFileKey
log caching: cachedFileKey.sourceFile if cachedFileKey.verbose
fs.writeFileSync fileName, JSON.stringify merge {source, compiled, props}
cachedFileKey
###
IN: cachedFileKey (see above)
###
: (cachedFileKey) ->
start = currentSecond()
if (fileName = cachedFileKey) &&
fs.existsSync(fileName)
if (cacheContents = try JSON.parse fs.readFileSync fileName) &&
cacheContents.source == cachedFileKey.source &&
cachedFileKey, cacheContents.props
cacheContents.fromCache = true
log cached: "#{(currentSecond() - start) * 1000 | 0}ms " + cachedFileKey.sourceFile if cachedFileKey.verbose
cacheContents
# # debug caching:
# else
# log CompileCache:
# cacheFileInvalid:
# cacheContents: cacheContents
# sourceMatch: cacheContents && cacheContents.source == cachedFileKey.source
# verifyDependencies: cacheContents && cachedFileKey, cacheContents.props
# null
: (cachedFileKey, props) ->
if moduleDependencies = props?.moduleDependencies
for sourceString, cachedRequireString of moduleDependencies
{requireString} = findModuleSync sourceString, sourceFile: cachedFileKey.sourceFile
if requireString != cachedRequireString
log CompileCache:
message: "moduleDependencies changed"
sourceString: sourceString
sourceFile: cachedFileKey.sourceFile
require:
old: cachedRequireString
new: requireString
return false
true
else
true
# NOTE: for some reason when using mock-fs, we need to apply (Promise.resolve item) to glob's results
: (verbose) ->
glob + "*"
.then (list) ->
Promise.all array list, (item) ->
Promise.resolve item
.then (item) -> fs.unlink item
.tap -> log "cache-reset: ".gray + item.green + " (deleted)".gray if verbose