Skip to content

Commit 6eb3cd7

Browse files
committed
Merge pull request #116 from postcss/resolve-id
Refactor path resolve
2 parents fd2b1dd + 9c32c3c commit 6eb3cd7

File tree

6 files changed

+171
-136
lines changed

6 files changed

+171
-136
lines changed

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
- Removed: async mode/option
22
([#107](https://github.com/postcss/postcss-import/pull/107))
3+
- Removed: "bower_components" not supported by default anymore, use "path" option to add it back
4+
([#116](https://github.com/postcss/postcss-import/pull/116))
5+
- Changed: custom resolve has more responsibility for paths resolving.
6+
See [resolve option](https://github.com/postcss/postcss-import#resolve) for more information about this change
7+
([#116](https://github.com/postcss/postcss-import/pull/116))
38

49
# 7.1.3 - 2015-11-05
510

README.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
55
This plugin can consume local files, node modules or bower packages.
66
To resolve path of an `@import` rule, it can look into root directory
7-
(by default `process.cwd()`), `web_modules`, `node_modules`, `bower_components`
7+
(by default `process.cwd()`), `web_modules`, `node_modules`
88
or local modules.
99
_When importing a module, it will looks for `index.css` or file referenced in
1010
`package.json` in the `style` field._
@@ -144,7 +144,9 @@ Set to `true` if you want @import rules to parse glob patterns.
144144
Type: `Function`
145145
Default: `null`
146146

147-
You can overwrite the default path resolving way by setting this option, using the `resolve.sync(id, opts)` signature that [resolve.sync](https://github.com/substack/node-resolve#resolvesyncid-opts) has.
147+
You can overwrite the default path resolving way by setting this option.
148+
This function gets `(id, basedir, importOptions)` arguments and returns full path or promise resolving full path.
149+
You can use [resolve](https://github.com/substack/node-resolve) for that.
148150

149151
#### `skipDuplicates`
150152

index.js

Lines changed: 77 additions & 119 deletions
Original file line numberDiff line numberDiff line change
@@ -5,20 +5,18 @@ 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")
10-
var helpers = require("postcss-message-helpers")
119
var glob = require("glob")
1210
var parseImports = require("./lib/parse-imports")
1311
var resolveMedia = require("./lib/resolve-media")
12+
var resolveId = require("./lib/resolve-id")
1413

1514
/**
1615
* Constants
1716
*/
1817
var moduleDirectories = [
1918
"web_modules",
2019
"node_modules",
21-
"bower_components",
2220
]
2321

2422
/**
@@ -67,7 +65,6 @@ function AtImport(options) {
6765

6866
var state = {
6967
importedFiles: {},
70-
ignoredAtRules: [],
7168
hashFiles: {},
7269
}
7370
if (opts.from) {
@@ -85,8 +82,8 @@ function AtImport(options) {
8582
createProcessor(result, options.plugins)
8683
)
8784

88-
function onParseEnd() {
89-
addIgnoredAtRulesOnTop(styles, state.ignoredAtRules)
85+
function onParseEnd(ignored) {
86+
addIgnoredAtRulesOnTop(styles, ignored)
9087

9188
if (
9289
typeof opts.addDependencyTo === "object" &&
@@ -141,20 +138,32 @@ function parseStyles(
141138
})
142139

143140
var importResults = imports.map(function(instance) {
144-
return helpers.try(function transformAtImport() {
145-
return readAtImport(
146-
result,
147-
instance.node,
148-
instance,
149-
options,
150-
state,
151-
media,
152-
processor
153-
)
154-
}, instance.node.source)
141+
return readAtImport(
142+
result,
143+
instance.node,
144+
instance,
145+
options,
146+
state,
147+
media,
148+
processor
149+
)
155150
})
156151

157-
return Promise.all(importResults)
152+
return Promise.all(importResults).then(function(result) {
153+
// Flatten ignored instances
154+
return result.reduce(function(ignored, item) {
155+
if (Array.isArray(item)) {
156+
item = item.filter(function(instance) {
157+
return instance
158+
})
159+
ignored = ignored.concat(item)
160+
}
161+
else if (item) {
162+
ignored.push(item)
163+
}
164+
return ignored
165+
}, [])
166+
})
158167
}
159168

160169
/**
@@ -212,15 +221,14 @@ function parseGlob(imports, instance, options) {
212221
* @param {Array} state
213222
*/
214223
function addIgnoredAtRulesOnTop(styles, ignoredAtRules) {
215-
var i = ignoredAtRules.length
216-
if (i) {
217-
while (i--) {
218-
var ignored = ignoredAtRules[i][1]
219-
ignored.node.params = ignored.fullUri +
220-
(ignored.media.length ? " " + ignored.media.join(", ") : "")
221-
222-
styles.prepend(ignored.node)
223-
}
224+
var i = ignoredAtRules.length - 1
225+
while (i !== -1) {
226+
var ignored = ignoredAtRules[i]
227+
ignored.node.params = ignored.fullUri +
228+
(ignored.media.length ? " " + ignored.media.join(", ") : "")
229+
230+
styles.prepend(ignored.node)
231+
i -= 1
224232
}
225233
}
226234

@@ -247,51 +255,54 @@ function readAtImport(
247255
if (parsedAtImport.uri.match(/^(?:[a-z]+:)?\/\//i)) {
248256
parsedAtImport.media = media
249257

250-
// save
251-
state.ignoredAtRules.push([ atRule, parsedAtImport ])
252-
253258
// detach
254259
atRule.remove()
255260

256-
return Promise.resolve()
261+
return Promise.resolve(parsedAtImport)
257262
}
258263

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

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()
270+
return Promise.resolve().then(function() {
271+
if (options.resolve) {
272+
return options.resolve(parsedAtImport.uri, dir, options)
276273
}
274+
return resolveId(parsedAtImport.uri, dir, options.path)
275+
}).then(function(resolvedFilename) {
276+
if (options.skipDuplicates) {
277+
// skip files already imported at the same scope
278+
if (
279+
state.importedFiles[resolvedFilename] &&
280+
state.importedFiles[resolvedFilename][media]
281+
) {
282+
atRule.remove()
283+
return Promise.resolve()
284+
}
277285

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

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

297308
/**
@@ -368,14 +379,18 @@ function readImportedContent(
368379
processor
369380
)
370381

371-
return parsedResult.then(function() {
382+
var instances
383+
384+
return parsedResult.then(function(result) {
385+
instances = result
372386
return processor.process(newStyles)
373387
})
374388
.then(function(newResult) {
375389
result.messages = result.messages.concat(newResult.messages)
376390
})
377391
.then(function() {
378392
insertRules(atRule, parsedAtImport, newStyles)
393+
return instances
379394
})
380395
}
381396

@@ -414,63 +429,6 @@ function insertRules(atRule, parsedAtImport, newStyles) {
414429
atRule.replaceWith(newNodes)
415430
}
416431

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-
474432
/**
475433
* Read the contents of a file
476434
*

lib/resolve-id.js

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
var fs = require("fs")
2+
var path = require("path")
3+
var resolve = require("resolve")
4+
5+
var moduleDirectories = [
6+
"web_modules",
7+
"node_modules",
8+
]
9+
10+
function isFilePromise(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 resolvePromise(id, opts) {
25+
return new Promise(function(res, rej) {
26+
resolve(id, opts, function(err, path) {
27+
if (err) {
28+
return rej(err)
29+
}
30+
res(path)
31+
})
32+
})
33+
}
34+
35+
module.exports = function(id, base, paths) {
36+
var resolveOpts = {
37+
basedir: base,
38+
moduleDirectory: moduleDirectories,
39+
paths: paths,
40+
extensions: [ ".css" ],
41+
packageFilter: function processPackage(pkg) {
42+
pkg.main = pkg.style || "index.css"
43+
return pkg
44+
},
45+
}
46+
return resolvePromise(id, resolveOpts).catch(function() {
47+
// fix to try relative files on windows with "./"
48+
// if it's look like it doesn't start with a relative path already
49+
// if (id.match(/^\.\.?/)) {throw e}
50+
return resolvePromise("./" + id, resolveOpts)
51+
}).catch(function() {
52+
// LAST HOPE
53+
return Promise.all(paths.map(function(p) {
54+
return isFilePromise(path.resolve(p, id))
55+
})).then(function(results) {
56+
for (var i = 0; i < results.length; i += 1) {
57+
if (results[i]) {
58+
return path.resolve(paths[i], id)
59+
}
60+
}
61+
62+
throw new Error([
63+
"Failed to find '" + id + "'",
64+
"in [ ",
65+
" " + paths.join(",\n "),
66+
"]",
67+
].join("\n "))
68+
})
69+
})
70+
}

package.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@
2020
"glob": "^5.0.14",
2121
"object-assign": "^4.0.1",
2222
"postcss": "^5.0.2",
23-
"postcss-message-helpers": "^2.0.0",
2423
"postcss-value-parser": "^3.2.3",
2524
"resolve": "^1.1.6"
2625
},

0 commit comments

Comments
 (0)