-
Notifications
You must be signed in to change notification settings - Fork 2
/
index.js
70 lines (54 loc) · 1.99 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
'use strict';
const { debuglog } = require('util');
const Walker = require('node-source-walk');
const gonzales = require('gonzales-pe');
const debug = debuglog('detective-less');
/**
* Extract the @import statements from a given less file's content
*
* @param {String} content
* @param {Object} options
* @param {Boolean} options.url - detect any url() references to images, fonts, etc.
* @return {String[]}
*/
module.exports = function detective(content, options = {}) {
if (content === undefined) throw new Error('content not given');
if (typeof content !== 'string') throw new Error('content is not a string');
let ast = {};
try {
debug('content: %s', content);
ast = gonzales.parse(content, { syntax: 'less' });
} catch (error) {
debug('parse error: %s', error.message);
}
detective.ast = ast;
const walker = new Walker();
let dependencies = [];
walker.walk(ast, node => {
if (isImportStatement(node)) {
dependencies = [...dependencies, ...extractDependencies(node)];
return;
}
if (options?.url && node.type === 'uri') {
dependencies = [...dependencies, ...extractUriDependencies(node)];
}
});
return dependencies;
};
function isImportStatement(node) {
if (!node || node.type !== 'atrule') return false;
if (node.content.length === 0 || node.content[0].type !== 'atkeyword') return false;
const atKeyword = node.content[0];
if (atKeyword.content.length === 0) return false;
return ['ident', 'import'].includes(atKeyword.content[0].type);
}
function extractDependencies(importStatementNode) {
return importStatementNode.content
.filter(innerNode => ['string', 'ident'].includes(innerNode.type))
.map(identifierNode => identifierNode.content.replaceAll(/["']/g, ''));
}
function extractUriDependencies(importStatementNode) {
return importStatementNode.content
.filter(innerNode => ['string', 'ident', 'raw'].includes(innerNode.type))
.map(identifierNode => identifierNode.content.replaceAll(/["']/g, ''));
}