mirror of
https://github.com/atom/atom.git
synced 2026-01-23 05:48:10 -05:00
path.join calls path.normalize and with the number of paths being pushed through the cache it is faster to require them manually since they are already normalized
252 lines
8.0 KiB
CoffeeScript
252 lines
8.0 KiB
CoffeeScript
Module = require 'module'
|
|
path = require 'path'
|
|
|
|
fs = null # Defer so the cache is used
|
|
semver = null # Defer so the cache is used
|
|
|
|
nativeModules = process.binding('natives')
|
|
|
|
cache =
|
|
builtins: {}
|
|
debug: false
|
|
dependencies: {}
|
|
extensions: {}
|
|
folders: {}
|
|
ranges: {}
|
|
registered: false
|
|
resourcePath: null
|
|
|
|
requireDependencies = ->
|
|
fs ?= require 'fs-plus'
|
|
semver ?= require 'semver'
|
|
|
|
# isAbsolute is inlined from fs-plus so that fs-plust itself can be required
|
|
# from this cache.
|
|
if process.platform is 'win32'
|
|
isAbsolute = (pathToCheck) ->
|
|
pathToCheck and (pathToCheck[1] is ':' or (pathToCheck[0] is '\\' and pathToCheck[1] is '\\'))
|
|
else
|
|
isAbsolute = (pathToCheck) ->
|
|
pathToCheck and pathToCheck[0] is '/'
|
|
|
|
loadDependencies = (modulePath, rootPath, rootMetadata, moduleCache) ->
|
|
for childPath in fs.listSync(path.join(modulePath, 'node_modules'))
|
|
continue if path.basename(childPath) is '.bin'
|
|
continue if rootPath is modulePath and rootMetadata.packageDependencies?.hasOwnProperty(path.basename(childPath))
|
|
|
|
childMetadataPath = path.join(childPath, 'package.json')
|
|
continue unless fs.isFileSync(childMetadataPath)
|
|
|
|
childMetadata = JSON.parse(fs.readFileSync(childMetadataPath))
|
|
if childMetadata?.version
|
|
try
|
|
mainPath = require.resolve(childPath)
|
|
|
|
if mainPath
|
|
moduleCache.dependencies.push
|
|
name: childMetadata.name
|
|
version: childMetadata.version
|
|
path: path.relative(rootPath, mainPath)
|
|
|
|
loadDependencies(childPath, rootPath, rootMetadata, moduleCache)
|
|
|
|
return
|
|
|
|
loadFolderCompatibility = (modulePath, rootPath, rootMetadata, moduleCache) ->
|
|
metadataPath = path.join(modulePath, 'package.json')
|
|
return unless fs.isFileSync(metadataPath)
|
|
|
|
dependencies = JSON.parse(fs.readFileSync(metadataPath))?.dependencies ? {}
|
|
|
|
for name, version of dependencies
|
|
try
|
|
new semver.Range(version)
|
|
catch error
|
|
delete dependencies[name]
|
|
|
|
onDirectory = (childPath) ->
|
|
path.basename(childPath) isnt 'node_modules'
|
|
|
|
extensions = ['.js', '.coffee', '.json', '.node']
|
|
paths = {}
|
|
onFile = (childPath) ->
|
|
if path.extname(childPath) in extensions
|
|
relativePath = path.relative(rootPath, path.dirname(childPath))
|
|
paths[relativePath] = true
|
|
fs.traverseTreeSync(modulePath, onFile, onDirectory)
|
|
|
|
paths = Object.keys(paths)
|
|
if paths.length > 0 and Object.keys(dependencies).length > 0
|
|
moduleCache.folders.push({paths, dependencies})
|
|
|
|
for childPath in fs.listSync(path.join(modulePath, 'node_modules'))
|
|
continue if path.basename(childPath) is '.bin'
|
|
continue if rootPath is modulePath and rootMetadata.packageDependencies?.hasOwnProperty(path.basename(childPath))
|
|
|
|
loadFolderCompatibility(childPath, rootPath, rootMetadata, moduleCache)
|
|
|
|
return
|
|
|
|
satisfies = (version, rawRange) ->
|
|
unless parsedRange = cache.ranges[rawRange]
|
|
parsedRange = new semver.Range(rawRange)
|
|
cache.ranges[rawRange] = parsedRange
|
|
parsedRange.test(version)
|
|
|
|
resolveFilePath = (relativePath, parentModule) ->
|
|
return unless relativePath
|
|
return unless parentModule?.filename
|
|
return unless relativePath[0] is '.' or isAbsolute(relativePath)
|
|
return if relativePath[relativePath.length - 1] is '/'
|
|
|
|
resolvedPath = path.resolve(path.dirname(parentModule.filename), relativePath)
|
|
if resolvedPath.indexOf(cache.resourcePath) is 0
|
|
extension = path.extname(resolvedPath)
|
|
if extension
|
|
return resolvedPath if cache.extensions[extension]?.has(resolvedPath)
|
|
else
|
|
for extension, paths of cache.extensions
|
|
resolvedPathWithExtension = "#{resolvedPath}#{extension}"
|
|
return resolvedPathWithExtension if paths.has(resolvedPathWithExtension)
|
|
|
|
return
|
|
|
|
resolveModulePath = (relativePath, parentModule) ->
|
|
return unless relativePath
|
|
return unless parentModule?.filename
|
|
|
|
return if nativeModules.hasOwnProperty(relativePath)
|
|
return if relativePath[0] is '.'
|
|
return if relativePath[relativePath.length - 1] is '/'
|
|
return if isAbsolute(relativePath)
|
|
|
|
folderPath = path.dirname(parentModule.filename)
|
|
|
|
range = cache.folders[folderPath]?[relativePath]
|
|
unless range?
|
|
if builtinPath = cache.builtins[relativePath]
|
|
return builtinPath
|
|
else
|
|
return
|
|
|
|
candidates = cache.dependencies[relativePath]
|
|
return unless candidates?
|
|
|
|
for version, resolvedPath of candidates
|
|
if Module._cache.hasOwnProperty(resolvedPath) or resolvedPath.indexOf(cache.resourcePath) is 0
|
|
return resolvedPath if satisfies(version, range)
|
|
|
|
return
|
|
|
|
registerBuiltins = (devMode) ->
|
|
if devMode
|
|
cache.builtins.atom = path.join(cache.resourcePath, 'exports', 'atom.coffee')
|
|
else
|
|
cache.builtins.atom = path.join(cache.resourcePath, 'exports', 'atom.js')
|
|
|
|
atomShellRoot = path.resolve(window.location.pathname, '..', '..', '..', 'atom')
|
|
|
|
commonRoot = path.join(atomShellRoot, 'common', 'api', 'lib')
|
|
commonBuiltins = ['callbacks-registry', 'clipboard', 'crash-reporter', 'screen', 'shell']
|
|
for builtin in commonBuiltins
|
|
cache.builtins[builtin] = path.join(commonRoot, "#{builtin}.js")
|
|
|
|
rendererRoot = path.join(atomShellRoot, 'renderer', 'api', 'lib')
|
|
rendererBuiltins = ['ipc', 'remote']
|
|
for builtin in rendererBuiltins
|
|
cache.builtins[builtin] = path.join(rendererRoot, "#{builtin}.js")
|
|
|
|
if cache.debug
|
|
cache.findPathCount = 0
|
|
cache.findPathTime = 0
|
|
cache.loadCount = 0
|
|
cache.requireTime = 0
|
|
global.moduleCache = cache
|
|
|
|
originalLoad = Module::load
|
|
Module::load = ->
|
|
cache.loadCount++
|
|
originalLoad.apply(this, arguments)
|
|
|
|
originalRequire = Module::require
|
|
Module::require = ->
|
|
startTime = Date.now()
|
|
exports = originalRequire.apply(this, arguments)
|
|
cache.requireTime += Date.now() - startTime
|
|
exports
|
|
|
|
originalFindPath = Module._findPath
|
|
Module._findPath = (request, paths) ->
|
|
cacheKey = JSON.stringify({request, paths})
|
|
cache.findPathCount++ unless Module._pathCache[cacheKey]
|
|
|
|
startTime = Date.now()
|
|
foundPath = originalFindPath.apply(global, arguments)
|
|
cache.findPathTime += Date.now() - startTime
|
|
foundPath
|
|
|
|
exports.create = (modulePath) ->
|
|
requireDependencies()
|
|
|
|
modulePath = fs.realpathSync(modulePath)
|
|
metadataPath = path.join(modulePath, 'package.json')
|
|
metadata = JSON.parse(fs.readFileSync(metadataPath))
|
|
|
|
moduleCache =
|
|
version: 1
|
|
dependencies: []
|
|
folders: []
|
|
|
|
loadDependencies(modulePath, modulePath, metadata, moduleCache)
|
|
loadFolderCompatibility(modulePath, modulePath, metadata, moduleCache)
|
|
|
|
metadata._atomModuleCache = moduleCache
|
|
fs.writeFileSync(metadataPath, JSON.stringify(metadata, null, 2))
|
|
|
|
return
|
|
|
|
exports.register = ({resourcePath, devMode}={}) ->
|
|
return if cache.registered
|
|
|
|
originalResolveFilename = Module._resolveFilename
|
|
Module._resolveFilename = (relativePath, parentModule) ->
|
|
resolvedPath = resolveModulePath(relativePath, parentModule)
|
|
resolvedPath ?= resolveFilePath(relativePath, parentModule)
|
|
resolvedPath ? originalResolveFilename(relativePath, parentModule)
|
|
|
|
cache.registered = true
|
|
cache.resourcePath = resourcePath
|
|
registerBuiltins(devMode)
|
|
requireDependencies()
|
|
|
|
return
|
|
|
|
exports.add = (directoryPath, metadata) ->
|
|
# path.join isn't used in this function for speed since path.join calls
|
|
# path.normalize and all the paths are already normalized here.
|
|
|
|
unless metadata?
|
|
try
|
|
metadata = require("#{directoryPath}#{path.sep}package.json")
|
|
catch error
|
|
return
|
|
|
|
cacheToAdd = metadata?._atomModuleCache
|
|
for dependency in cacheToAdd?.dependencies ? []
|
|
cache.dependencies[dependency.name] ?= {}
|
|
cache.dependencies[dependency.name][dependency.version] ?= "#{directoryPath}#{path.sep}#{dependency.path}"
|
|
|
|
for entry in cacheToAdd?.folders ? []
|
|
for folderPath in entry.paths
|
|
cache.folders["#{directoryPath}#{path.sep}#{folderPath}"] = entry.dependencies
|
|
|
|
if directoryPath is cache.resourcePath
|
|
for extension, paths of cacheToAdd?.extensions
|
|
cache.extensions[extension] ?= new Set()
|
|
for filePath in paths
|
|
cache.extensions[extension].add("#{directoryPath}#{path.sep}#{filePath}")
|
|
|
|
return
|
|
|
|
exports.cache = cache
|