import './root' // break cyclical dependency deadlock – #87
import CssSyntaxError from './css-syntax-error';
// import PreviousMap from './previous-map';
let sequence = 0;
/**
* @typedef {object} filePosition
* @property {string} file - path to file
* @property {number} line - source line in file
* @property {number} column - source column in file
*/
/**
* Represents the source CSS.
*
* @example
* const root = postcss.parse(css, { from: file });
* const input = root.source.input;
*/
class Input {
/**
* @param {string} css - input CSS source
* @param {object} [opts] - {@link Processor#process} options
*/
constructor(css, opts = { }) {
/**
* @member {string} - input CSS source
*
* @example
* const input = postcss.parse('a{}', { from: file }).input;
* input.css //=> "a{}";
*/
this.css = css.toString();
if ( this.css[0] === '\uFEFF' || this.css[0] === '\uFFFE' ) {
this.css = this.css.slice(1);
}
if ( opts.from ) {
if ( /^\w+:\/\//.test(opts.from) ) {
/**
* @member {string} - The absolute path to the CSS source file
* defined with the `from` option.
*
* @example
* const root = postcss.parse(css, { from: 'a.css' });
* root.source.input.file //=> '/home/ai/a.css'
*/
this.file = opts.from;
} else {
this.file = path.resolve(opts.from);
}
}
/*
let map = new PreviousMap(this.css, opts);
if ( map.text ) {
/!**
* @member {PreviousMap} - The input source map passed from
* a compilation step before PostCSS
* (for example, from Sass compiler).
*
* @example
* root.source.input.map.consumer().sources //=> ['a.sass']
*!/
this.map = map;
let file = map.consumer().file;
if ( !this.file && file ) this.file = this.mapResolve(file);
}
*/
if ( !this.file ) {
sequence += 1;
/**
* @member {string} - The unique ID of the CSS source. It will be
* created if `from` option is not provided
* (because PostCSS does not know the file path).
*
* @example
* const root = postcss.parse(css);
* root.source.input.file //=> undefined
* root.source.input.id //=> ""
*/
this.id = '';
}
if ( this.map ) this.map.file = this.from;
}
error(message, line, column, opts = { }) {
let result;
let origin = this.origin(line, column);
if ( origin ) {
result = new CssSyntaxError(message, origin.line, origin.column,
origin.source, origin.file, opts.plugin);
} else {
result = new CssSyntaxError(message, line, column,
this.css, this.file, opts.plugin);
}
result.input = { line, column, source: this.css };
if ( this.file ) result.input.file = this.file;
return result;
}
/**
* Reads the input source map and returns a symbol position
* in the input source (e.g., in a Sass file that was compiled
* to CSS before being passed to PostCSS).
*
* @param {number} line - line in input CSS
* @param {number} column - column in input CSS
*
* @return {filePosition} position in input source
*
* @example
* root.source.input.origin(1, 1) //=> { file: 'a.css', line: 3, column: 1 }
*/
origin(line, column) {
if ( !this.map ) return false;
let consumer = this.map.consumer();
let from = consumer.originalPositionFor({ line, column });
if ( !from.source ) return false;
let result = {
file: this.mapResolve(from.source),
line: from.line,
column: from.column
};
let source = consumer.sourceContentFor(from.source);
if ( source ) result.source = source;
return result;
}
mapResolve(file) {
if ( /^\w+:\/\//.test(file) ) {
return file;
} else {
return path.resolve(this.map.consumer().sourceRoot || '.', file);
}
}
/**
* The CSS source identifier. Contains {@link Input#file} if the user
* set the `from` option, or {@link Input#id} if they did not.
* @type {string}
*
* @example
* const root = postcss.parse(css, { from: 'a.css' });
* root.source.input.from //=> "/home/ai/a.css"
*
* const root = postcss.parse(css);
* root.source.input.from //=> ""
*/
get from() {
return this.file || this.id;
}
}
export default Input;