Skip to content

Commit

Permalink
fix(source): improve source api
Browse files Browse the repository at this point in the history
now looks like source(baseDir, globs[, options])(__filename)
  • Loading branch information
estrattonbailey committed May 22, 2021
1 parent cf455be commit e8e533f
Showing 1 changed file with 153 additions and 147 deletions.
300 changes: 153 additions & 147 deletions source-filesystem.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,185 +25,191 @@ function createUrlFromFilepath ({ filepath, baseDir }) {
return filepath.split(baseDir)[1].split('.')[0]
}

function createDefaultExtensions ({ baseDir }) {
return {
default (filepath) {
const p = createUrlFromFilepath({ filepath, baseDir })
const defaultExtensions = {
default (filepath, baseDir) {
const p = createUrlFromFilepath({ filepath, baseDir })

return {
[p]: fs.readFileSync(filepath, 'utf8')
}
return {
[p]: fs.readFileSync(filepath, 'utf8')
}
}
}

function source (globs, { baseDir = cwd, file: root, extensions = {} } = {}) {
assert(path.isAbsolute(root), 'file should be an absolute path')
assert(path.isAbsolute(baseDir), 'baseDir should be an absolute path')

/*
* Current process variables
*/
const rootCounter = rootCounters[pid]
const sourcesToRootsMap = sourcesToRootsMaps[pid]
const rootsToSourcesMap = rootsToSourcesMaps[pid]
const rootsToGlobsMap = rootsToGlobsMaps[pid]

const config = getCurrentConfig() // current process config
const { env, emitter } = config

/*
* Normalize values
*/
baseDir = path.resolve(path.dirname(root), baseDir)
globs = [].concat(globs).map(g => path.resolve(baseDir, g))
extensions = {
...createDefaultExtensions({ baseDir }),
...extensions
}
function source (baseDir, globs, { extensions = {} } = {}) {
return root => {
assert(
path.isAbsolute(root),
'root file should be an absolute path — did you use __filename?'
)

debug('source', { baseDir, globs })

/*
* Sourced files
*/
const filepaths = globs
.map(glob => matched.sync(glob, { cwd: baseDir }))
.flat()
.map(fp => path.resolve(baseDir, fp))

/*
* If we're in watch mode, set up listeners
*/
if (env === 'development') {
rootsToGlobsMap[root] = globs

rootCounter[root] = rootCounter[root] || {
prevCallCount: 0,
callCount: 0
}
/*
* Current process variables
*/
const rootCounter = rootCounters[pid]
const sourcesToRootsMap = sourcesToRootsMaps[pid]
const rootsToSourcesMap = rootsToSourcesMaps[pid]
const rootsToGlobsMap = rootsToGlobsMaps[pid]

function processNewSource (s, r) {
sourcesToRootsMap[s] = sourcesToRootsMap[s] || new Set()
sourcesToRootsMap[s].add(r)
const config = getCurrentConfig() // current process config
const { env, emitter } = config

rootsToSourcesMap[r] = rootsToSourcesMap[r] || new Set()
rootsToSourcesMap[r].add(s)
/*
* Normalize values
*/
baseDir = path.resolve(path.dirname(root), baseDir)
globs = [].concat(globs).map(g => path.resolve(baseDir, g))
extensions = {
...defaultExtensions,
...extensions
}

/**
* Map sourced file to the root that sourced it.
* Call this EVERY render to source new filepaths.
debug('source', { baseDir, globs })

/*
* Sourced files
*/
for (const source of filepaths) {
processNewSource(source, root)
}
const filepaths = globs
.map(glob => matched.sync(glob, { cwd: baseDir }))
.flat()
.map(fp => path.resolve(baseDir, fp))

if (!ready) {
ready = true
/*
* If we're in watch mode, set up listeners
*/
if (env === 'development') {
rootsToGlobsMap[root] = globs

/*
* Clean up all source filepaths on remove of root file
*/
function cleanupOnRootRemoval (currentRoot) {
// first clear the deleted root file from any filepaths
for (const source of rootsToSourcesMap[currentRoot]) {
sourcesToRootsMap[source].delete(currentRoot)
}
rootCounter[root] = rootCounter[root] || {
prevCallCount: 0,
callCount: 0
}

function processNewSource (s, r) {
sourcesToRootsMap[s] = sourcesToRootsMap[s] || new Set()
sourcesToRootsMap[s].add(r)

delete rootsToSourcesMap[currentRoot]
delete rootCounter[currentRoot]
delete rootsToGlobsMap[currentRoot]
rootsToSourcesMap[r] = rootsToSourcesMap[r] || new Set()
rootsToSourcesMap[r].add(s)
}

/*
* These emitters are set up in watch.js, the main watcher. They
* watch the root filepaths (pages) themselves.
/**
* Map sourced file to the root that sourced it.
* Call this EVERY render to source new filepaths.
*/
emitter.on('done', ([file]) => {
const currentRoot = Object.keys(rootCounter).find(root => root === file)

if (currentRoot) {
const cache = rootCounter[currentRoot]

/*
* If source() hasn't been called again, it's been deleted
* or commented out.
*/
if (cache.callCount === cache.prevCallCount) {
cleanupOnRootRemoval(currentRoot)
} else {
for (const source of filepaths) {
processNewSource(source, root)
}

if (!ready) {
ready = true

/*
* Clean up all source filepaths on remove of root file
*/
function cleanupOnRootRemoval (currentRoot) {
// first clear the deleted root file from any filepaths
for (const source of rootsToSourcesMap[currentRoot]) {
sourcesToRootsMap[source].delete(currentRoot)
}

delete rootsToSourcesMap[currentRoot]
delete rootCounter[currentRoot]
delete rootsToGlobsMap[currentRoot]
}

/*
* These emitters are set up in watch.js, the main watcher. They
* watch the root filepaths (pages) themselves.
*/
emitter.on('done', ([file]) => {
const currentRoot = Object.keys(rootCounter).find(
root => root === file
)

if (currentRoot) {
const cache = rootCounter[currentRoot]

/*
* Inc prev call count for this particular root file
* so that it's ready for next render
* If source() hasn't been called again, it's been deleted
* or commented out.
*/
rootCounter[currentRoot].prevCallCount =
rootCounter[currentRoot].callCount
if (cache.callCount === cache.prevCallCount) {
cleanupOnRootRemoval(currentRoot)
} else {
/*
* Inc prev call count for this particular root file
* so that it's ready for next render
*/
rootCounter[currentRoot].prevCallCount =
rootCounter[currentRoot].callCount
}
}
}
})
emitter.on('remove', ([file]) => {
const currentRoot = Object.keys(rootCounter).find(root => root === file)
if (currentRoot) cleanupOnRootRemoval(currentRoot)
})
}
})
emitter.on('remove', ([file]) => {
const currentRoot = Object.keys(rootCounter).find(
root => root === file
)
if (currentRoot) cleanupOnRootRemoval(currentRoot)
})
}

// init filewatcher for sourced filepaths
if (!watcher) {
watcher = chokidar.watch(baseDir, {
ignoreInitial: true
})
watcher.on('all', (event, filepath) => {
if (fs.existsSync(filepath) && fs.lstatSync(filepath).isDirectory())
return

if (/add|change/.test(event)) {
for (const r of Object.keys(rootsToGlobsMap)) {
if (match.isMatch(filepath, rootsToGlobsMap[r])) {
processNewSource(filepath, r)
// init filewatcher for sourced filepaths
if (!watcher) {
watcher = chokidar.watch(baseDir, {
ignoreInitial: true
})
watcher.on('all', (event, filepath) => {
if (fs.existsSync(filepath) && fs.lstatSync(filepath).isDirectory())
return

if (/add|change/.test(event)) {
for (const r of Object.keys(rootsToGlobsMap)) {
if (match.isMatch(filepath, rootsToGlobsMap[r])) {
processNewSource(filepath, r)
}
}
}
}

const roots = Array.from(sourcesToRootsMap[filepath] || [])
if (roots.length) buildFiles(roots, config)
const roots = Array.from(sourcesToRootsMap[filepath] || [])
if (roots.length) buildFiles(roots, config)

// if remove, remove last
if (event === 'unlink') {
for (const r of Object.keys(rootsToGlobsMap)) {
if (match.isMatch(filepath, rootsToGlobsMap[r])) {
sourcesToRootsMap[filepath].delete(r)
rootsToSourcesMap[r].delete(filepath)
// if remove, remove last
if (event === 'unlink') {
for (const r of Object.keys(rootsToGlobsMap)) {
if (match.isMatch(filepath, rootsToGlobsMap[r])) {
sourcesToRootsMap[filepath].delete(r)
rootsToSourcesMap[r].delete(filepath)
}
}
}
}
})
})
}

/*
* Inc call count for this particular root file
*/
++rootCounter[root].callCount
}

/*
* Inc call count for this particular root file
* Outside watch mode, just read filepaths and return
*/
++rootCounter[root].callCount
}

/*
* Outside watch mode, just read filepaths and return
*/

const results = filepaths.map(fp => {
const extension = path.extname(fp).split('.')[1]
const handler = extensions[extension] // try specific extension
return handler ? handler(fp) : extensions.default(fp)
})

return {
filepaths,
paths: results.reduce((paths, res) => {
return paths.concat(Object.keys(res))
}, []),
sources: results.reduce((sources, res) => {
return Object.assign(sources, res)
}, {})
const results = filepaths.map(fp => {
const extension = path.extname(fp).split('.')[1]
const handler = extensions[extension] // try specific extension
return handler ? handler(fp, baseDir) : extensions.default(fp, baseDir)
})

return {
filepaths,
paths: results.reduce((paths, res) => {
return paths.concat(Object.keys(res))
}, []),
sources: results.reduce((sources, res) => {
return Object.assign(sources, res)
}, {})
}
}
}

Expand Down

0 comments on commit e8e533f

Please sign in to comment.