UNPKG

@stylable/core

Version:

CSS for Components

277 lines 12.6 kB
"use strict"; 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