Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions lib/__tests__/__snapshots__/parseJs.test.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ exports[`comment between tag function and template literal. with interpolation 1
},
],
"locationStart": {
"column": 56,
"column": 66,
"line": 1,
},
"rangeEnd": 79,
Expand All @@ -26,7 +26,7 @@ exports[`comment between tag function and template literal. without interpolatio
"css": "color: red;",
"interpolationRanges": [],
"locationStart": {
"column": 56,
"column": 66,
"line": 1,
},
"rangeEnd": 76,
Expand Down Expand Up @@ -113,7 +113,7 @@ exports[`component notations passing a function 1`] = `
"css": "color: red;",
"interpolationRanges": [],
"locationStart": {
"column": 37,
"column": 38,
"line": 1,
},
"rangeEnd": 48,
Expand All @@ -128,7 +128,7 @@ exports[`component notations passing a function 2 1`] = `
"css": "color: red;",
"interpolationRanges": [],
"locationStart": {
"column": 40,
"column": 41,
"line": 1,
},
"rangeEnd": 51,
Expand Down
8 changes: 3 additions & 5 deletions lib/__tests__/isStyledComponent.test.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
let ts = require('typescript');
let { parseSync } = require('../oxc');
let { isStyledComponent } = require('../isStyledComponent');

describe('isStyledComponent', () => {
/**
* @param {string} code
*/
function getExpression(code) {
const sourceFile = ts.createSourceFile('temp.ts', code, ts.ScriptTarget.Latest, true);

// @ts-ignore
const expression = sourceFile.statements[0].expression;
let result = parseSync('temp.ts', code, { sourceType: 'unambiguous' });
let expression = result.program.body[0].expression;

return expression;
}
Expand Down
70 changes: 39 additions & 31 deletions lib/isStyledComponent.js
Original file line number Diff line number Diff line change
@@ -1,38 +1,39 @@
let ts = require('typescript');

/**
* Checkes where it is a styled component or css`` interpolation
* Checks whether it is a styled component or css`` interpolation
*
* @param {ts.Node} node
* @param {any} node
* @return {boolean}
*/
module.exports.isStyledComponent = function isStyledComponent(node) {
if (ts.isTaggedTemplateExpression(node)) {
if (node.type === 'TaggedTemplateExpression') {
// styled.foo``
if (ts.isPropertyAccessExpression(node.tag)) {
return isStyledIdentifier(node.tag.expression);
if (node.tag.type === 'MemberExpression' && !node.tag.computed) {
return isStyledIdentifier(node.tag.object);
}

if (ts.isCallExpression(node.tag)) {
if (node.tag.type === 'CallExpression') {
// styled(Component)``
if (isStyledIdentifier(node.tag.expression)) {
if (isStyledIdentifier(node.tag.callee)) {
return true;
}

// styled.foo.attrs({})``
if (
ts.isPropertyAccessExpression(node.tag.expression) &&
ts.isPropertyAccessExpression(node.tag.expression.expression) &&
isStyledIdentifier(node.tag.expression.expression.expression)
node.tag.callee.type === 'MemberExpression' &&
!node.tag.callee.computed &&
node.tag.callee.object.type === 'MemberExpression' &&
!node.tag.callee.object.computed &&
isStyledIdentifier(node.tag.callee.object.object)
) {
return true;
}

// styled(Component).attrs({})``
if (
ts.isPropertyAccessExpression(node.tag.expression) &&
ts.isCallExpression(node.tag.expression.expression) &&
isStyledIdentifier(node.tag.expression.expression.expression)
node.tag.callee.type === 'MemberExpression' &&
!node.tag.callee.computed &&
node.tag.callee.object.type === 'CallExpression' &&
isStyledIdentifier(node.tag.callee.object.callee)
) {
return true;
}
Expand All @@ -41,31 +42,30 @@ module.exports.isStyledComponent = function isStyledComponent(node) {
}

// css`` or createGlobalStyle``
if (ts.isIdentifier(node.tag)) {
return node.tag.text === 'css' || node.tag.text === 'createGlobalStyle';
if (node.tag.type === 'Identifier') {
return node.tag.name === 'css' || node.tag.name === 'createGlobalStyle';
}
}

// styled.foo(props => ``)
if (
ts.isCallExpression(node) &&
ts.isPropertyAccessExpression(node.expression) &&
isStyledIdentifier(node.expression.expression) &&
ts.isArrowFunction(node.arguments[0]) &&
(ts.isNoSubstitutionTemplateLiteral(node.arguments[0].body) ||
ts.isTemplateExpression(node.arguments[0].body))
node.type === 'CallExpression' &&
node.callee.type === 'MemberExpression' &&
!node.callee.computed &&
isStyledIdentifier(node.callee.object) &&
node.arguments[0]?.type === 'ArrowFunctionExpression' &&
isTemplateLiteral(node.arguments[0].body)
) {
return true;
}

// styled(Component)(props => ``)
if (
ts.isCallExpression(node) &&
ts.isCallExpression(node.expression) &&
isStyledIdentifier(node.expression.expression) &&
ts.isArrowFunction(node.arguments[0]) &&
(ts.isNoSubstitutionTemplateLiteral(node.arguments[0].body) ||
ts.isTemplateExpression(node.arguments[0].body))
node.type === 'CallExpression' &&
node.callee.type === 'CallExpression' &&
isStyledIdentifier(node.callee.callee) &&
node.arguments[0]?.type === 'ArrowFunctionExpression' &&
isTemplateLiteral(node.arguments[0].body)
) {
return true;
}
Expand All @@ -74,9 +74,17 @@ module.exports.isStyledComponent = function isStyledComponent(node) {
};

/**
* @param {ts.LeftHandSideExpression} expression
* @param {any} expression
* @return {boolean}
*/
function isStyledIdentifier(expression) {
return ts.isIdentifier(expression) && expression.text === 'styled';
return expression.type === 'Identifier' && expression.name === 'styled';
}

/**
* @param {any} node
* @return {boolean}
*/
function isTemplateLiteral(node) {
return node.type === 'TemplateLiteral';
}
54 changes: 54 additions & 0 deletions lib/oxc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// CJS wrapper for oxc-parser's native binding
// oxc-parser is ESM-only, so we load the native binding directly

let binding = loadBinding();

function loadBinding() {
let pkg = require('oxc-parser/package.json');

for (let name of Object.keys(pkg.optionalDependencies || {})) {
try {
return require(name);
} catch {
// Not installed for this platform, try next
}
}

throw new Error('oxc-parser: no native binding found for this platform');
}

/**
* @param {string} filename
* @param {string} sourceText
* @param {Record<string, any>} [options]
* @returns {{ program: any, errors: Array<any>, comments: Array<any> }}
*/
module.exports.parseSync = function parseSync(filename, sourceText, options) {
let result = binding.parseSync(filename, sourceText, options || {});

let { node: program, fixes } = JSON.parse(result.program);

for (let fixPath of fixes) {
let node = program;

for (let key of fixPath) {
node = node[key];
}

if (node.bigint) {
node.value = BigInt(node.bigint);
} else {
try {
node.value = RegExp(node.regex.pattern, node.regex.flags);
} catch {
// Invalid regexp
}
}
}

return {
program,
errors: result.errors,
comments: result.comments,
};
};
Loading