@stylable/core
Version:
CSS for Components
277 lines • 12.6 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.createImportSymbol = exports.getImportStatements = exports.StylablePublicApi = exports.hooks = exports.diagnostics = exports.ImportTypeHook = exports.PseudoImportDecl = exports.PseudoImport = void 0;
const feature_1 = require("./feature");
const diagnostics_1 = require("./diagnostics");
const STSymbol = __importStar(require("./st-symbol"));
const plugable_record_1 = require("../helpers/plugable-record");
const import_1 = require("../helpers/import");
const css_custom_property_1 = require("../helpers/css-custom-property");
const path_1 = __importDefault(require("path"));
const diagnostics_2 = require("../diagnostics");
exports.PseudoImport = `:import`;
exports.PseudoImportDecl = {
DEFAULT: `-st-default`,
NAMED: `-st-named`,
FROM: `-st-from`,
};
/**
* ImportTypeHook is used as a way to cast imported symbols before resolving their actual type.
* currently used only for `keyframes` as they are completely on a separate namespace from other symbols.
*
* Hooks are registered statically since the features are static and cannot be selected/disabled.
* If the system will ever change to support picking features dynamically, this mechanism would
* have to move into the `metaInit` hook.
*/
exports.ImportTypeHook = new Map();
const dataKey = plugable_record_1.plugableRecord.key('imports');
exports.diagnostics = {
...import_1.parseImportMessages,
FORBIDDEN_DEF_IN_COMPLEX_SELECTOR: diagnostics_1.generalDiagnostics.FORBIDDEN_DEF_IN_COMPLEX_SELECTOR,
NO_ST_IMPORT_IN_NESTED_SCOPE: (0, diagnostics_2.createDiagnosticReporter)('05011', 'error', () => `cannot use "@st-import" inside of nested scope`),
NO_PSEUDO_IMPORT_IN_NESTED_SCOPE: (0, diagnostics_2.createDiagnosticReporter)('05012', 'error', () => `cannot use ":import" inside of nested scope`),
INVALID_CUSTOM_PROPERTY_AS_VALUE: (0, diagnostics_2.createDiagnosticReporter)('05013', 'error', (name, as) => `invalid alias for custom property "${name}" as "${as}"; custom properties must be prefixed with "--" (double-dash)`),
UNKNOWN_IMPORTED_SYMBOL: (0, diagnostics_2.createDiagnosticReporter)('05015', 'error', (name, path) => `cannot resolve imported symbol "${name}" from stylesheet "${path}"`),
UNKNOWN_IMPORTED_FILE: (0, diagnostics_2.createDiagnosticReporter)('05016', 'error', (path, error) => `cannot resolve imported file: "${path}"${error ? `\nFailed with:\n${error}` : ''}`),
UNKNOWN_TYPED_IMPORT: (0, diagnostics_2.createDiagnosticReporter)('05018', 'error', (type) => `Unknown type import "${type}"`),
NO_DEFAULT_EXPORT: (0, diagnostics_2.createDiagnosticReporter)('05020', 'error', (path) => `Native CSS files have no default export. Imported file: "${path}"`),
UNSUPPORTED_NATIVE_IMPORT: (0, diagnostics_2.createDiagnosticReporter)('05021', 'warning', () => `Unsupported @import within imported native CSS file`),
};
// HOOKS
exports.hooks = (0, feature_1.createFeature)({
metaInit({ meta }) {
plugable_record_1.plugableRecord.set(meta.data, dataKey, []);
},
analyzeInit(context) {
const imports = plugable_record_1.plugableRecord.getUnsafe(context.meta.data, dataKey);
const dirContext = path_1.default.dirname(context.meta.source);
// collect shallow imports
for (const node of context.meta.sourceAst.nodes) {
if (!isImportStatement(node)) {
continue;
}
const parsedImport = node.type === `atrule`
? (0, import_1.parseStImport)(node, dirContext, context.diagnostics)
: (0, import_1.parsePseudoImport)(node, dirContext, context.diagnostics);
imports.push(parsedImport);
addImportSymbols(parsedImport, context, dirContext);
}
},
analyzeAtRule({ context, atRule }) {
if (atRule.name === `st-import` && atRule.parent?.type !== `root`) {
context.diagnostics.report(exports.diagnostics.NO_ST_IMPORT_IN_NESTED_SCOPE(), {
node: atRule,
});
}
else if (atRule.name === `import` && context.meta.type === 'css') {
context.diagnostics.report(exports.diagnostics.UNSUPPORTED_NATIVE_IMPORT(), {
node: atRule,
});
}
},
analyzeSelectorNode({ context, rule, node }) {
if (node.value !== `import`) {
return;
}
if (rule.selector !== `:import`) {
context.diagnostics.report(exports.diagnostics.FORBIDDEN_DEF_IN_COMPLEX_SELECTOR(exports.PseudoImport), { node: rule });
return;
}
if (rule.parent?.type !== `root`) {
context.diagnostics.report(exports.diagnostics.NO_PSEUDO_IMPORT_IN_NESTED_SCOPE(), {
node: rule,
});
}
},
prepareAST({ node, toRemove }) {
if (isImportStatement(node)) {
toRemove.push(node);
}
},
transformInit({ context }) {
validateImports(context);
calcCssDepth(context);
},
});
// API
class StylablePublicApi {
constructor(stylable) {
this.stylable = stylable;
}
analyze(meta) {
return getImportStatements(meta).map(({ request, defaultExport, named, keyframes }) => ({
from: request,
default: defaultExport,
named,
typed: {
keyframes,
},
}));
}
}
exports.StylablePublicApi = StylablePublicApi;
function calcCssDepth(context) {
let cssDepth = 1;
const deepDependencies = (0, import_1.tryCollectImportsDeep)(context.resolver, context.meta, new Set(), ({ depth, request }) => {
if (request.endsWith('.css')) {
cssDepth = Math.max(cssDepth, depth);
}
}, 2);
context.meta.transformCssDepth = { cssDepth, deepDependencies };
}
function isImportStatement(node) {
return ((node.type === `atrule` && node.name === `st-import`) ||
(node.type === `rule` && node.selector === `:import`));
}
function getImportStatements({ data }) {
const state = plugable_record_1.plugableRecord.getUnsafe(data, dataKey);
return state;
}
exports.getImportStatements = getImportStatements;
function createImportSymbol(importDef, type, name, dirContext) {
return {
_kind: 'import',
type: type === 'default' ? `default` : `named`,
name: type === `default` ? name : importDef.named[name],
import: importDef,
context: dirContext,
};
}
exports.createImportSymbol = createImportSymbol;
// internal
function addImportSymbols(importDef, context, dirContext) {
checkForInvalidAsUsage(importDef, context);
if (importDef.defaultExport) {
STSymbol.addSymbol({
context,
localName: importDef.defaultExport,
symbol: createImportSymbol(importDef, `default`, `default`, dirContext),
node: importDef.rule,
});
}
Object.keys(importDef.named).forEach((name) => {
STSymbol.addSymbol({
context,
localName: name,
symbol: createImportSymbol(importDef, `named`, name, dirContext),
node: importDef.rule,
});
});
// import as typed symbol
for (const [type, imports] of Object.entries(importDef.typed)) {
const handler = exports.ImportTypeHook.get(type);
if (handler) {
for (const [localName, importName] of Object.entries(imports)) {
handler(context, localName, importName, importDef);
}
}
else {
context.diagnostics.report(exports.diagnostics.UNKNOWN_TYPED_IMPORT(type), {
node: importDef.rule,
word: type,
});
}
}
}
function checkForInvalidAsUsage(importDef, context) {
for (const [local, imported] of Object.entries(importDef.named)) {
if ((0, css_custom_property_1.validateCustomPropertyName)(imported) && !(0, css_custom_property_1.validateCustomPropertyName)(local)) {
context.diagnostics.report(exports.diagnostics.INVALID_CUSTOM_PROPERTY_AS_VALUE(imported, local), { node: importDef.rule });
}
}
}
function validateImports(context) {
const imports = plugable_record_1.plugableRecord.getUnsafe(context.meta.data, dataKey);
for (const importObj of imports) {
const entity = context.resolver.getModule(importObj);
if (!entity.value) {
// warn about unknown imported files
const fromDecl = importObj.rule.nodes &&
importObj.rule.nodes.find((decl) => decl.type === 'decl' && decl.prop === exports.PseudoImportDecl.FROM);
context.diagnostics.report(exports.diagnostics.UNKNOWN_IMPORTED_FILE(importObj.request, getErrorText(entity)), {
node: fromDecl || importObj.rule,
word: importObj.request,
});
}
else if (entity.kind === 'css') {
const meta = entity.value;
// propagate some native CSS diagnostics to st-import
if (meta.type === 'css') {
let foundUnsupportedNativeImport = false;
for (const report of meta.diagnostics.reports) {
if (report.code === '05021') {
foundUnsupportedNativeImport = true;
break;
}
}
if (foundUnsupportedNativeImport) {
context.diagnostics.report(exports.diagnostics.UNSUPPORTED_NATIVE_IMPORT(), {
node: importObj.rule,
word: importObj.defaultExport,
});
}
}
// report unsupported native CSS default import
if (meta.type !== 'stylable' && importObj.defaultExport) {
context.diagnostics.report(exports.diagnostics.NO_DEFAULT_EXPORT(importObj.request), {
node: importObj.rule,
word: importObj.defaultExport,
});
}
// warn about unknown named imported symbols
for (const name in importObj.named) {
const origName = importObj.named[name];
const resolvedSymbol = context.resolver.resolveImported(importObj, origName);
if (resolvedSymbol === null || !resolvedSymbol.symbol) {
const namedDecl = importObj.rule.nodes &&
importObj.rule.nodes.find((decl) => decl.type === 'decl' && decl.prop === exports.PseudoImportDecl.NAMED);
context.diagnostics.report(exports.diagnostics.UNKNOWN_IMPORTED_SYMBOL(origName, importObj.request), { node: namedDecl || importObj.rule, word: origName });
}
}
}
else if (entity.kind === 'js') {
// TODO: add diagnostics for JS imports (typeof checks)
}
}
}
function getErrorText(res) {
if ('error' in res) {
const { error } = res;
if (typeof error === 'object' && error) {
return 'details' in error
? String(error.details)
: 'message' in error
? String(error.message)
: String(error);
}
return String(error);
}
return '';
}
//# sourceMappingURL=st-import.js.map