Skip to content

Commit d26b4fa

Browse files
committed
Refactor path resolve
1 parent fd2b1dd commit d26b4fa

File tree

8 files changed

+141
-153
lines changed

8 files changed

+141
-153
lines changed

index.js

Lines changed: 40 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,12 @@ var fs = require("fs")
55
var path = require("path")
66

77
var assign = require("object-assign")
8-
var resolve = require("resolve")
98
var postcss = require("postcss")
109
var helpers = require("postcss-message-helpers")
1110
var glob = require("glob")
1211
var parseImports = require("./lib/parse-imports")
1312
var resolveMedia = require("./lib/resolve-media")
13+
var resolveId = require("./lib/resolve-id")
1414

1515
/**
1616
* Constants
@@ -257,41 +257,50 @@ function readAtImport(
257257
}
258258

259259
addInputToPath(options)
260-
var resolvedFilename = resolveFilename(
261-
parsedAtImport.uri,
262-
options.root,
263-
options.path,
264-
atRule.source,
265-
options.resolve
266-
)
260+
var base = atRule.source && atRule.source.input && atRule.source.input.file
261+
? path.dirname(path.resolve(options.root, atRule.source.input.file))
262+
: options.root
267263

268-
if (options.skipDuplicates) {
269-
// skip files already imported at the same scope
270-
if (
271-
state.importedFiles[resolvedFilename] &&
272-
state.importedFiles[resolvedFilename][media]
273-
) {
274-
atRule.remove()
275-
return Promise.resolve()
264+
return Promise.resolve().then(function() {
265+
if (options.resolve) {
266+
return options.resolve(parsedAtImport.uri, base, options)
276267
}
268+
return resolveId(
269+
parsedAtImport.uri,
270+
base,
271+
options.path
272+
)
273+
}).then(function(resolvedFilename) {
274+
if (options.skipDuplicates) {
275+
// skip files already imported at the same scope
276+
if (
277+
state.importedFiles[resolvedFilename] &&
278+
state.importedFiles[resolvedFilename][media]
279+
) {
280+
atRule.remove()
281+
return Promise.resolve()
282+
}
277283

278-
// save imported files to skip them next time
279-
if (!state.importedFiles[resolvedFilename]) {
280-
state.importedFiles[resolvedFilename] = {}
284+
// save imported files to skip them next time
285+
if (!state.importedFiles[resolvedFilename]) {
286+
state.importedFiles[resolvedFilename] = {}
287+
}
288+
state.importedFiles[resolvedFilename][media] = true
281289
}
282-
state.importedFiles[resolvedFilename][media] = true
283-
}
284290

285-
return readImportedContent(
286-
result,
287-
atRule,
288-
parsedAtImport,
289-
assign({}, options),
290-
resolvedFilename,
291-
state,
292-
media,
293-
processor
294-
)
291+
return readImportedContent(
292+
result,
293+
atRule,
294+
parsedAtImport,
295+
assign({}, options),
296+
resolvedFilename,
297+
state,
298+
media,
299+
processor
300+
)
301+
}).catch(function(err) {
302+
result.warn(err.message, { node: atRule })
303+
})
295304
}
296305

297306
/**
@@ -414,63 +423,6 @@ function insertRules(atRule, parsedAtImport, newStyles) {
414423
atRule.replaceWith(newNodes)
415424
}
416425

417-
/**
418-
* Check if a file exists
419-
*
420-
* @param {String} name
421-
*/
422-
function resolveFilename(name, root, paths, source, resolver) {
423-
var dir = source && source.input && source.input.file
424-
? path.dirname(path.resolve(root, source.input.file))
425-
: root
426-
427-
try {
428-
var resolveOpts = {
429-
basedir: dir,
430-
moduleDirectory: moduleDirectories.concat(paths),
431-
paths: paths,
432-
extensions: [ ".css" ],
433-
packageFilter: function processPackage(pkg) {
434-
pkg.main = pkg.style || "index.css"
435-
return pkg
436-
},
437-
}
438-
var file
439-
resolver = resolver || resolve.sync
440-
try {
441-
file = resolver(name, resolveOpts)
442-
}
443-
catch (e) {
444-
// fix to try relative files on windows with "./"
445-
// if it's look like it doesn't start with a relative path already
446-
// if (name.match(/^\.\.?/)) {throw e}
447-
try {
448-
file = resolver("./" + name, resolveOpts)
449-
}
450-
catch (err) {
451-
// LAST HOPE
452-
if (!paths.some(function(dir2) {
453-
file = path.join(dir2, name)
454-
return fs.existsSync(file)
455-
})) {
456-
throw err
457-
}
458-
}
459-
}
460-
461-
return path.normalize(file)
462-
}
463-
catch (e) {
464-
throw new Error(
465-
"Failed to find '" + name + "' from " + root +
466-
"\n in [ " +
467-
"\n " + paths.join(",\n ") +
468-
"\n ]",
469-
source
470-
)
471-
}
472-
}
473-
474426
/**
475427
* Read the contents of a file
476428
*

lib/resolve-id.js

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
var fs = require("fs")
2+
var path = require("path")
3+
var resolveModule = require("resolve")
4+
5+
var moduleDirectories = [
6+
"web_modules",
7+
"node_modules",
8+
]
9+
10+
function isFile(path) {
11+
return new Promise(function(resolve, reject) {
12+
fs.stat(path, function(err, stat) {
13+
if (err) {
14+
if (err.code === "ENOENT") {
15+
return resolve(false)
16+
}
17+
return reject(err)
18+
}
19+
resolve(stat.isFile() || stat.isFIFO())
20+
})
21+
})
22+
}
23+
24+
function getFirstExistingFile(files) {
25+
return files.reduce(function(promise, file) {
26+
return promise.then(function(resolvedFile) {
27+
if (resolvedFile) {
28+
return resolvedFile
29+
}
30+
return isFile(file).then(function(is) {
31+
if (is) {
32+
return file
33+
}
34+
})
35+
})
36+
}, Promise.resolve())
37+
}
38+
39+
module.exports = function(id, base, paths) {
40+
var files = [ base ].concat(paths).reduce(function(files, p) {
41+
files.push(path.resolve(p, id))
42+
files.push(path.resolve(p, id + ".css"))
43+
files.push(path.resolve(p, id, "index.css"))
44+
return files
45+
}, [])
46+
47+
return getFirstExistingFile(files).then(function(existing) {
48+
if (existing) {
49+
return existing
50+
}
51+
var opts = {
52+
basedir: path.resolve(base),
53+
moduleDirectory: moduleDirectories,
54+
extensions: [ ".css" ],
55+
packageFilter: function(pkg) {
56+
if (!/\.css$/.test(pkg.main)) {
57+
pkg.main = pkg.style || "index.css"
58+
}
59+
return pkg
60+
},
61+
}
62+
63+
return new Promise(function(resolve) {
64+
resolveModule(id, opts, function(err, existing) {
65+
if (err) {
66+
return resolve()
67+
}
68+
resolve(existing)
69+
})
70+
})
71+
}).then(function(existing) {
72+
if (existing) {
73+
return existing
74+
}
75+
throw new Error([
76+
"Failed to find '" + id + "'",
77+
"in [ ",
78+
" " + paths.join(",\n "),
79+
"]",
80+
].join("\n "))
81+
})
82+
}

test/fixtures/ignore.expected.css

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,3 @@
1-
@import "http://css" (min-width: 25em);
2-
3-
@import "http://css-screen" (min-width: 25em) and screen;
41

52
@import "http://css";
63

@@ -28,6 +25,10 @@
2825

2926
@import url(//css);
3027

28+
@import "http://css" (min-width: 25em);
29+
30+
@import "http://css-screen" (min-width: 25em) and screen;
31+
3132
@media (min-width: 25em){
3233
ignore{}
3334
}

test/fixtures/imports/local-module/main.css

Lines changed: 0 additions & 1 deletion
This file was deleted.

test/fixtures/imports/local-module/package.json

Lines changed: 0 additions & 1 deletion
This file was deleted.

test/fixtures/modules.css

Lines changed: 0 additions & 19 deletions
This file was deleted.

test/fixtures/modules.expected.css

Lines changed: 0 additions & 21 deletions
This file was deleted.

test/import.js

Lines changed: 15 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import test from "ava"
2+
import path from "path"
23
import { readFileSync } from "fs"
34
import postcss from "postcss"
45
import atImport from ".."
@@ -54,12 +55,6 @@ test("should not need `path` option if `source` option has been passed", t => {
5455
})
5556
})
5657

57-
test("should be able to consume npm package or local modules", t => {
58-
return compareFixtures(t, "modules", {
59-
root: ".",
60-
})
61-
})
62-
6358
test("should not fail with only one absolute import", t => {
6459
var base = "@import url(http://)"
6560
return postcss()
@@ -85,13 +80,11 @@ test("should output readable trace", t => {
8580
return postcss()
8681
.use(atImport())
8782
.process(readFileSync(file), { from: file })
88-
.catch(error => {
89-
t.throws(
90-
() => {
91-
throw error
92-
},
83+
.then(result => {
84+
t.is(
85+
result.warnings()[0].text,
9386
/* eslint-disable max-len */
94-
/import-missing.css:2:5: Failed to find 'missing-file.css' from .*\n\s+in \[/gm
87+
"Failed to find 'missing-file.css'\n in [ \n " + path.resolve("fixtures/imports") + "\n ]"
9588
/* eslint-enabme max-len */
9689
)
9790
})
@@ -155,14 +148,16 @@ test("should work with no styles without throwing an error", t => {
155148

156149
test("should be able to consume modules in the custom-resolve way", t => {
157150
const resolve = require("resolve")
158-
const sassResolve = (file, opts) => {
159-
opts = opts || {}
160-
opts.extensions = [ ".scss", ".css" ]
161-
opts.packageFilter = pkg => {
162-
pkg.main = pkg.sass || pkg.style || "index"
163-
return pkg
164-
}
165-
return resolve.sync(file, opts)
151+
const sassResolve = (id, base, importOptions) => {
152+
return resolve.sync(id, {
153+
basedir: base,
154+
extensions: [ ".scss", ".css" ],
155+
paths: importOptions.path,
156+
packageFilter: pkg => {
157+
pkg.main = pkg.sass || pkg.style || "index"
158+
return pkg
159+
},
160+
})
166161
}
167162
return compareFixtures(t, "custom-resolve-modules", {
168163
root: ".",

0 commit comments

Comments
 (0)