Housekeeping node tests, renamed to unit-tests (#4936)

Add parsers for the background css shorthand property, make ViewBase unit testable in node environment

Add background parser and linear-gradient parser

Use sticky regexes

Simplify some types, introduce generic Parsed<T> instead of & TokenRange

Apply each parser to return a { start, end, value } object

Move the css selector parser to the css/parser and unify types

Add the first steps toward building homegrown css parser

Add somewhat standards compliant tokenizer, add baseline, rework and shady css parsers

Enable all tests again, skip flaky perf test

Improve css parser tokenizer by converting some char token types to simple string

Implement 'parse a stylesheet'

Add gonzales css-parser

Add parseLib and css-tree perf

Add a thin parser layer that will convert CSS3 tokens to values, for now output is compatible with rework

Make root tsc green

Return the requires of tns-core-modules to use relative paths for webpack to work

Implement support for '@import 'url-string';

Fix function parser, function-token is no-longer neglected

Make the style-scope be able to load from "css" and "css-ast" modules

Add a loadAppCss event so theme can be added to snapshot separately from loaded
This commit is contained in:
Panayot Cankov
2017-10-20 10:42:07 +03:00
committed by Hristo Hristov
parent 2eba7c66e4
commit f7a3a36b9c
64 changed files with 2875 additions and 1281 deletions

View File

@@ -17,7 +17,7 @@ export function hasLaunched(): boolean {
export { Observable };
import { UnhandledErrorEventData, iOSApplication, AndroidApplication, CssChangedEventData } from ".";
import { UnhandledErrorEventData, iOSApplication, AndroidApplication, CssChangedEventData, LoadAppCSSEventData } from ".";
export const launchEvent = "launch";
export const suspendEvent = "suspend";
@@ -66,6 +66,10 @@ export function getCssFileName(): string {
return cssFile;
}
export function loadAppCss(): void {
events.notify(<LoadAppCSSEventData>{ eventName: "loadAppCss", object: app, cssFile: getCssFileName() });
}
export function addCss(cssText: string): void {
events.notify(<CssChangedEventData>{ eventName: "cssChanged", object: app, cssText: cssText });
}

View File

@@ -141,6 +141,14 @@ export function setCssFileName(cssFile: string): void;
*/
export function getCssFileName(): string;
/**
* Loads immediately the app.css.
* By default the app.css file is loaded shortly after "loaded".
* For the Android snapshot the CSS can be parsed during the snapshot generation,
* as the CSS does not depend on runtime APIs, and loadAppCss will be called explicitly.
*/
export function loadAppCss();
export function addCss(cssText: string): void;
/**
@@ -553,3 +561,7 @@ export function getNativeApplication(): any;
* Indicates if the application is allready launched. See also the `application.on("launch", handler)` event.
*/
export function hasLaunched(): boolean;
export interface LoadAppCSSEventData extends EventData {
cssFile: string;
}

View File

@@ -1,8 +1,14 @@
import { iOSApplication as IOSApplicationDefinition, LaunchEventData, ApplicationEventData, OrientationChangedEventData } from ".";
import {
iOSApplication as IOSApplicationDefinition,
LaunchEventData,
ApplicationEventData,
OrientationChangedEventData,
LoadAppCSSEventData
} from ".";
import {
notify, launchEvent, resumeEvent, suspendEvent, exitEvent, lowMemoryEvent,
orientationChangedEvent, setApplication, livesync, displayedEvent
orientationChangedEvent, setApplication, livesync, displayedEvent, getCssFileName
} from "./application-common";
// First reexport so that app module is initialized.
@@ -117,6 +123,7 @@ class IOSApplication implements IOSApplicationDefinition {
};
notify(args);
notify(<LoadAppCSSEventData>{ eventName: "loadAppCss", object: <any>this, cssFile: getCssFileName() });
let rootView = createRootView(args.root);
this._window.content = rootView;

View File

@@ -5,6 +5,8 @@ const enum MessageType {
error = 3
}
declare function __time(): number;
function __message(message: any, level: string) {
if ((<any>global).__consoleMessage) {
(<any>global).__consoleMessage(message, level);
@@ -227,7 +229,7 @@ export class Console {
}
private timeMillis() {
return java.lang.System.nanoTime() / 1000000; // 1 ms = 1000000 ns
return __time(); // 1 ms = 1000000 ns
}
public time(reportName: string): void {

View File

File diff suppressed because it is too large Load Diff

View File

@@ -19,20 +19,11 @@ function ensurePlatform() {
}
}
// we are defining these as private variables within the IO scope and will use them to access the corresponding properties for each FSEntity instance.
// this allows us to encapsulate (hide) the explicit property setters and force the users go through the exposed APIs to receive FSEntity instances.
var nameProperty = "_name";
var pathProperty = "_path";
var isKnownProperty = "_isKnown";
var fileLockedProperty = "_locked";
var extensionProperty = "_extension";
var lastModifiedProperty = "_lastModified";
var createFile = function (info: { path: string; name: string; extension: string }) {
var file = new File();
file[pathProperty] = info.path;
file[nameProperty] = info.name;
file[extensionProperty] = info.extension;
file._path = info.path;
file._name = info.name;
file._extension = info.extension;
return file;
};
@@ -50,13 +41,20 @@ var createFolder = function (info: { path: string; name: string; }) {
var folder = new Folder();
folder[pathProperty] = info.path;
folder[nameProperty] = info.name;
folder._path = info.path;
folder._name = info.name;
return folder;
};
export class FileSystemEntity {
_path: string;
_name: string;
_extension: string;
_locked: boolean;
_lastModified: Date;
_isKnown: boolean;
get parent(): Folder {
var onError = function (error) {
throw error;
@@ -86,7 +84,7 @@ export class FileSystemEntity {
}
public removeSync(onError?: (error: any) => any): void {
if (this[isKnownProperty]) {
if (this._isKnown) {
if (onError) {
onError({ message: "Cannot delete known folder." });
}
@@ -120,7 +118,7 @@ export class FileSystemEntity {
}
public renameSync(newName: string, onError?: (error: any) => any): void {
if (this[isKnownProperty]) {
if (this._isKnown) {
if (onError) {
onError(new Error("Cannot rename known folder."));
}
@@ -149,26 +147,26 @@ export class FileSystemEntity {
}
fileAccess.rename(this.path, newPath, localError);
this[pathProperty] = newPath;
this[nameProperty] = newName;
this._path = newPath;
this._name = newName;
if (this instanceof File) {
this[extensionProperty] = fileAccess.getFileExtension(newPath);
this._extension = fileAccess.getFileExtension(newPath);
}
}
get name(): string {
return this[nameProperty];
return this._name;
}
get path(): string {
return this[pathProperty];
return this._path;
}
get lastModified(): Date {
var value = this[lastModifiedProperty];
if (!this[lastModifiedProperty]) {
value = this[lastModifiedProperty] = getFileAccess().getLastModified(this.path);
var value = this._lastModified;
if (!this._lastModified) {
value = this._lastModified = getFileAccess().getLastModified(this.path);
}
return value;
@@ -194,22 +192,22 @@ export class File extends FileSystemEntity {
}
get extension(): string {
return this[extensionProperty];
return this._extension;
}
get isLocked(): boolean {
// !! is a boolean conversion/cast, handling undefined as well
return !!this[fileLockedProperty];
return !!this._locked;
}
public readSync(onError?: (error: any) => any): any {
this.checkAccess();
this[fileLockedProperty] = true;
this._locked = true;
var that = this;
var localError = (error) => {
that[fileLockedProperty] = false;
that._locked = false;
if (onError) {
onError(error);
}
@@ -217,7 +215,7 @@ export class File extends FileSystemEntity {
var content = getFileAccess().read(this.path, localError);
this[fileLockedProperty] = false;
this._locked = false;
return content;
@@ -227,11 +225,11 @@ export class File extends FileSystemEntity {
this.checkAccess();
try {
this[fileLockedProperty] = true;
this._locked = true;
var that = this;
var localError = function (error) {
that[fileLockedProperty] = false;
that._locked = false;
if (onError) {
onError(error);
}
@@ -239,7 +237,7 @@ export class File extends FileSystemEntity {
getFileAccess().write(this.path, content, localError);
} finally {
this[fileLockedProperty] = false;
this._locked = false;
}
}
@@ -262,18 +260,18 @@ export class File extends FileSystemEntity {
public readTextSync(onError?: (error: any) => any, encoding?: string): string {
this.checkAccess();
this[fileLockedProperty] = true;
this._locked = true;
var that = this;
var localError = (error) => {
that[fileLockedProperty] = false;
that._locked = false;
if (onError) {
onError(error);
}
};
var content = getFileAccess().readText(this.path, localError, encoding);
this[fileLockedProperty] = false;
this._locked = false;
return content;
}
@@ -297,11 +295,11 @@ export class File extends FileSystemEntity {
this.checkAccess();
try {
this[fileLockedProperty] = true;
this._locked = true;
var that = this;
var localError = function (error) {
that[fileLockedProperty] = false;
that._locked = false;
if (onError) {
onError(error);
}
@@ -310,7 +308,7 @@ export class File extends FileSystemEntity {
// TODO: Asyncronous
getFileAccess().writeText(this.path, content, localError, encoding);
} finally {
this[fileLockedProperty] = false;
this._locked = false;
}
}
@@ -370,7 +368,7 @@ export class Folder extends FileSystemEntity {
}
get isKnown(): boolean {
return this[isKnownProperty];
return this._isKnown;
}
public getFile(name: string): File {
@@ -473,8 +471,8 @@ export module knownFolders {
if (!_documents) {
var path = getFileAccess().getDocumentsFolderPath();
_documents = new Folder();
_documents[pathProperty] = path;
_documents[isKnownProperty] = true;
_documents._path = path;
_documents._isKnown = true;
}
return _documents;
@@ -484,8 +482,8 @@ export module knownFolders {
if (!_temp) {
var path = getFileAccess().getTempFolderPath();
_temp = new Folder();
_temp[pathProperty] = path;
_temp[isKnownProperty] = true;
_temp._path = path;
_temp._isKnown = true;
}
return _temp;
@@ -495,8 +493,8 @@ export module knownFolders {
if (!_app) {
var path = getFileAccess().getCurrentAppPath();
_app = new Folder();
_app[pathProperty] = path;
_app[isKnownProperty] = true;
_app._path = path;
_app._isKnown = true;
}
return _app;
@@ -518,8 +516,8 @@ export module knownFolders {
if (existingFolderInfo) {
_library = existingFolderInfo.folder;
_library[pathProperty] = existingFolderInfo.path;
_library[isKnownProperty] = true;
_library._path = existingFolderInfo.path;
_library._isKnown = true;
}
}
@@ -534,8 +532,8 @@ export module knownFolders {
if (existingFolderInfo) {
_developer = existingFolderInfo.folder;
_developer[pathProperty] = existingFolderInfo.path;
_developer[isKnownProperty] = true;
_developer._path = existingFolderInfo.path;
_developer._isKnown = true;
}
}
@@ -550,8 +548,8 @@ export module knownFolders {
if (existingFolderInfo) {
_desktop = existingFolderInfo.folder;
_desktop[pathProperty] = existingFolderInfo.path;
_desktop[isKnownProperty] = true;
_desktop._path = existingFolderInfo.path;
_desktop._isKnown = true;
}
}
@@ -566,8 +564,8 @@ export module knownFolders {
if (existingFolderInfo) {
_downloads = existingFolderInfo.folder;
_downloads[pathProperty] = existingFolderInfo.path;
_downloads[isKnownProperty] = true;
_downloads._path = existingFolderInfo.path;
_downloads._isKnown = true;
}
}
@@ -582,8 +580,8 @@ export module knownFolders {
if (existingFolderInfo) {
_movies = existingFolderInfo.folder;
_movies[pathProperty] = existingFolderInfo.path;
_movies[isKnownProperty] = true;
_movies._path = existingFolderInfo.path;
_movies._isKnown = true;
}
}
@@ -598,8 +596,8 @@ export module knownFolders {
if (existingFolderInfo) {
_music = existingFolderInfo.folder;
_music[pathProperty] = existingFolderInfo.path;
_music[isKnownProperty] = true;
_music._path = existingFolderInfo.path;
_music._isKnown = true;
}
}
@@ -614,8 +612,8 @@ export module knownFolders {
if (existingFolderInfo) {
_pictures = existingFolderInfo.folder;
_pictures[pathProperty] = existingFolderInfo.path;
_pictures[isKnownProperty] = true;
_pictures._path = existingFolderInfo.path;
_pictures._isKnown = true;
}
}
@@ -630,15 +628,15 @@ export module knownFolders {
if (existingFolderInfo) {
_sharedPublic = existingFolderInfo.folder;
_sharedPublic[pathProperty] = existingFolderInfo.path;
_sharedPublic[isKnownProperty] = true;
_sharedPublic._path = existingFolderInfo.path;
_sharedPublic._isKnown = true;
}
}
return _sharedPublic;
};
function getExistingFolderInfo(pathDirectory: NSSearchPathDirectory): { folder: Folder; path: string } {
function getExistingFolderInfo(pathDirectory: any /* NSSearchPathDirectory */): { folder: Folder; path: string } {
var fileAccess = (<any>getFileAccess());
var folderPath = fileAccess.getKnownPath(pathDirectory);
var folderInfo = fileAccess.getExistingFolder(folderPath);

View File

@@ -2,15 +2,17 @@
require("./decorators");
// Required by V8 snapshot generator
global.__extends = global.__extends || function (d, b) {
for (var p in b) {
if (b.hasOwnProperty(p)) {
d[p] = b[p];
if (!global.__extends) {
global.__extends = function (d, b) {
for (var p in b) {
if (b.hasOwnProperty(p)) {
d[p] = b[p];
}
}
}
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
}
// This method iterates all the keys in the source exports object and copies them to the destination exports one.
// Note: the method will not check for naming collisions and will override any already existing entries in the destination exports.
@@ -26,6 +28,8 @@ import * as dialogsModule from "../ui/dialogs";
type ModuleLoader = (name?: string) => any;
const modules: Map<string, ModuleLoader> = new Map<string, ModuleLoader>();
(<any>global).moduleResolvers = [global.require];
global.registerModule = function(name: string, loader: ModuleLoader): void {
modules.set(name, loader);
}
@@ -38,10 +42,13 @@ global.loadModule = function(name: string): any {
const loader = modules.get(name);
if (loader) {
return loader();
} else {
let result = global.require(name);
modules.set(name, () => result);
return result;
}
for (let resolver of (<any>global).moduleResolvers) {
const result = resolver(name);
if (result) {
modules.set(name, () => result);
return result;
}
}
}
@@ -71,10 +78,6 @@ function registerOnGlobalContext(name: string, module: string): void {
get: function () {
// We do not need to cache require() call since it is already cached in the runtime.
let m = global.loadModule(module);
if (!__tnsGlobalMergedModules.has(module)) {
__tnsGlobalMergedModules.set(module, true);
global.moduleMerge(m, global);
}
// Redefine the property to make sure the above code is executed only once.
let resolvedValue = m[name];
@@ -106,6 +109,8 @@ export function install() {
alert: dialogs.alert,
confirm: dialogs.confirm,
prompt: dialogs.prompt,
login: dialogs.login,
action: dialogs.action,
XMLHttpRequest: xhr.XMLHttpRequest,
FormData: xhr.FormData,
@@ -124,12 +129,20 @@ export function install() {
registerOnGlobalContext("clearTimeout", "timer");
registerOnGlobalContext("setInterval", "timer");
registerOnGlobalContext("clearInterval", "timer");
registerOnGlobalContext("alert", "ui/dialogs");
registerOnGlobalContext("confirm", "ui/dialogs");
registerOnGlobalContext("prompt", "ui/dialogs");
registerOnGlobalContext("login", "ui/dialogs");
registerOnGlobalContext("action", "ui/dialogs");
registerOnGlobalContext("XMLHttpRequest", "xhr");
registerOnGlobalContext("FormData", "xhr");
registerOnGlobalContext("fetch", "fetch");
registerOnGlobalContext("Headers", "fetch");
registerOnGlobalContext("Request", "fetch");
registerOnGlobalContext("Response", "fetch");
// check whether the 'android' namespace is exposed
// if positive - the current device is an Android

View File

@@ -4,9 +4,10 @@ import * as platform from "../platform";
export class ImageAsset extends observable.Observable implements definition.ImageAsset {
private _options: definition.ImageAssetOptions;
private _ios: PHAsset;
private _nativeImage: any;
private _android: string; //file name of the image
ios: PHAsset;
android: string;
get options(): definition.ImageAssetOptions {
return this._options;
@@ -16,22 +17,6 @@ export class ImageAsset extends observable.Observable implements definition.Ima
this._options = value;
}
get ios(): PHAsset {
return this._ios;
}
set ios(value: PHAsset) {
this._ios = value;
}
get android(): string {
return this._android;
}
set android(value: string) {
this._android = value;
}
get nativeImage(): any {
return this._nativeImage;
}

View File

@@ -4,11 +4,21 @@ import * as common from "./image-asset-common";
global.moduleMerge(common, exports);
export class ImageAsset extends common.ImageAsset {
private _android: string; //file name of the image
constructor(asset: string) {
super();
this.android = asset;
}
get android(): string {
return this._android;
}
set android(value: string) {
this._android = value;
}
public getImageAsync(callback: (image, error) => void) {
let bitmapOptions = new android.graphics.BitmapFactory.Options();
bitmapOptions.inJustDecodeBounds = true;

View File

@@ -3,6 +3,8 @@ import * as common from "./image-asset-common";
global.moduleMerge(common, exports);
export class ImageAsset extends common.ImageAsset {
private _ios: PHAsset;
constructor(asset: PHAsset | UIImage) {
super();
if (asset instanceof UIImage) {
@@ -13,6 +15,14 @@ export class ImageAsset extends common.ImageAsset {
}
}
get ios(): PHAsset {
return this._ios;
}
set ios(value: PHAsset) {
this._ios = value;
}
public getImageAsync(callback: (image, error) => void) {
let srcWidth = this.nativeImage ? this.nativeImage.size.width : this.ios.pixelWidth;
let srcHeight = this.nativeImage ? this.nativeImage.size.height : this.ios.pixelHeight;

View File

@@ -1,11 +1,32 @@
declare var global: NodeJS.Global;
interface ModuleResolver {
/**
* A function used to resolve the exports for a module.
* @param uri The name of the module to be resolved.
*/
(uri: string): any;
}
//Augment the NodeJS global type with our own extensions
declare namespace NodeJS {
interface Global {
android?: any;
require(id: string): any;
registerModule(name: string, loader: ((name: string) => any)): void;
/**
* The NativeScript XML builder, style-scope, application modules use various resources such as:
* app.css, page.xml files and modules during the application life-cycle.
* The moduleResolvers can be used to provide additional mechanisms to locate such resources.
* For example:
* ```
* global.moduleResolvers.unshift(uri => uri === "main-page" ? require("main-page") : null);
* ```
* More advanced scenarios will allow for specific bundlers to integrate their module resolving mechanisms.
* When adding resolvers at the start of the array, avoid throwing and return null instead so subsequent resolvers may try to resolve the resource.
* By default the only member of the array is global.require, as last resort - if it fails to find a module it will throw.
*/
readonly moduleResolvers: ModuleResolver[];
loadModule(name: string): any;
moduleExists(name: string): boolean;
moduleMerge(sourceExports: any, destExports: any): void;

View File

@@ -10,6 +10,7 @@
"files": [
"**/*.d.ts",
"**/*.js",
"**/package.json",
"!android17.d.ts",
"!ios.d.ts",
"!bin/",

View File

@@ -85,7 +85,7 @@ export function isRunning(name: string): boolean {
return !!(info && info.runCount);
}
function countersProfileFunctionFactory<F extends Function>(fn: F, name: string): F {
function countersProfileFunctionFactory<F extends Function>(fn: F, name: string, type: MemberType = MemberType.Instance): F {
profileNames.push(name);
return <any>function() {
start(name);
@@ -97,8 +97,8 @@ function countersProfileFunctionFactory<F extends Function>(fn: F, name: string)
}
}
function timelineProfileFunctionFactory<F extends Function>(fn: F, name: string): F {
return <any>function() {
function timelineProfileFunctionFactory<F extends Function>(fn: F, name: string, type: MemberType = MemberType.Instance): F {
return type === MemberType.Instance ? <any>function() {
const start = time();
try {
return fn.apply(this, arguments);
@@ -106,10 +106,23 @@ function timelineProfileFunctionFactory<F extends Function>(fn: F, name: string)
const end = time();
console.log(`Timeline: Modules: ${name} ${this} (${start}ms. - ${end}ms.)`);
}
}
} : function() {
const start = time();
try {
return fn.apply(this, arguments);
} finally {
const end = time();
console.log(`Timeline: Modules: ${name} (${start}ms. - ${end}ms.)`);
}
};
}
let profileFunctionFactory: <F extends Function>(fn: F, name: string) => F;
const enum MemberType {
Static,
Instance
}
let profileFunctionFactory: <F extends Function>(fn: F, name: string, type?: MemberType) => F;
export function enable(mode: InstrumentationMode = "counters") {
profileFunctionFactory = mode && {
counters: countersProfileFunctionFactory,
@@ -154,7 +167,28 @@ const profileMethodUnnamed = (target, key, descriptor) => {
let name = className + key;
//editing the descriptor/value parameter
descriptor.value = profileFunctionFactory(originalMethod, name);
descriptor.value = profileFunctionFactory(originalMethod, name, MemberType.Instance);
// return edited descriptor as opposed to overwriting the descriptor
return descriptor;
}
const profileStaticMethodUnnamed = (ctor, key, descriptor) => {
// save a reference to the original method this way we keep the values currently in the
// descriptor and don't overwrite what another decorator might have done to the descriptor.
if (descriptor === undefined) {
descriptor = Object.getOwnPropertyDescriptor(ctor, key);
}
var originalMethod = descriptor.value;
let className = "";
if (ctor && ctor.name) {
className = ctor.name + ".";
}
let name = className + key;
//editing the descriptor/value parameter
descriptor.value = profileFunctionFactory(originalMethod, name, MemberType.Static);
// return edited descriptor as opposed to overwriting the descriptor
return descriptor;
@@ -188,6 +222,11 @@ export function profile(nameFnOrTarget?: string | Function | Object, fnOrKey?: F
return;
}
return profileMethodUnnamed(nameFnOrTarget, fnOrKey, descriptor);
} else if (typeof nameFnOrTarget === "function" && (typeof fnOrKey === "string" || typeof fnOrKey === "symbol")) {
if (!profileFunctionFactory) {
return;
}
return profileStaticMethodUnnamed(nameFnOrTarget, fnOrKey, descriptor);
} else if (typeof nameFnOrTarget === "string" && typeof fnOrKey === "function") {
if (!profileFunctionFactory) {
return fnOrKey;

View File

@@ -10,7 +10,7 @@ import { isString, isDefined } from "../../utils/types";
import { ComponentModule, setPropertyValue, getComponentModule } from "./component-builder";
import { platformNames, device } from "../../platform";
import { resolveFileName } from "../../file-system/file-name-resolver";
import { profile } from "tns-core-modules/profiling";
import { profile } from "../../profiling";
import * as traceModule from "../../trace";
const ios = platformNames.ios.toLowerCase();

View File

@@ -7,7 +7,7 @@ import { isEventOrGesture } from "../../core/bindable";
import { File, path, knownFolders } from "../../../file-system";
import { getBindingOptions, bindingConstants } from "../binding-builder";
import { resolveFileName } from "../../../file-system/file-name-resolver";
import { profile } from "tns-core-modules/profiling";
import { profile } from "../../../profiling";
import * as debugModule from "../../../utils/debug";
import * as platform from "../../../platform";

View File

@@ -32,7 +32,7 @@ export interface CssPropertyOptions<T extends Style, U> extends PropertyOptions<
export interface ShorthandPropertyOptions<P> {
readonly name: string,
readonly cssName: string;
readonly converter: (value: string | P) => [CssProperty<any, any>, any][],
readonly converter: (value: string | P) => [CssProperty<any, any> | CssAnimationProperty<any, any>, any][],
readonly getter: (this: Style) => string | P
}

View File

@@ -26,6 +26,7 @@ export * from "../bindable";
export * from "../properties";
import * as ssm from "../../styling/style-scope";
let styleScopeModule: typeof ssm;
function ensureStyleScopeModule() {
if (!styleScopeModule) {
@@ -588,7 +589,7 @@ export abstract class ViewBase extends Observable implements ViewBaseDefinition
// }
if (!nativeView) {
nativeView = <android.view.View>this.createNativeView();
nativeView = this.createNativeView();
}
this._androidView = nativeView;
@@ -597,7 +598,7 @@ export abstract class ViewBase extends Observable implements ViewBaseDefinition
this._isPaddingRelative = nativeView.isPaddingRelative();
}
let result: android.graphics.Rect = (<any>nativeView).defaultPaddings;
let result: any /* android.graphics.Rect */ = (<any>nativeView).defaultPaddings;
if (result === undefined) {
result = org.nativescript.widgets.ViewHelper.getPadding(nativeView);
(<any>nativeView).defaultPaddings = result;

View File

@@ -8,7 +8,7 @@ import { resolveFileName } from "../../file-system/file-name-resolver";
import { knownFolders, path } from "../../file-system";
import { parse, loadPage } from "../builder";
import * as application from "../../application";
import { profile } from "tns-core-modules/profiling";
import { profile } from "../../profiling";
export { application };

View File

@@ -715,6 +715,7 @@ class ActivityCallbacksImplementation implements AndroidActivityCallbacks {
private notifyLaunch(intent: android.content.Intent, savedInstanceState: android.os.Bundle): View {
const launchArgs: application.LaunchEventData = { eventName: application.launchEvent, object: application.android, android: intent, savedInstanceState };
application.notify(launchArgs);
application.notify(<application.LoadAppCSSEventData>{ eventName: "loadAppCss", object: <any>this, cssFile: application.getCssFileName() });
return launchArgs.root;
}

View File

@@ -8,7 +8,7 @@ import { FrameBase, View, application, layout, traceEnabled, traceWrite, traceCa
import { _createIOSAnimatedTransitioning } from "./fragment.transitions";
// HACK: Webpack. Use a fully-qualified import to allow resolve.extensions(.ios.js) to
// kick in. `../utils` doesn't seem to trigger the webpack extensions mechanism.
import * as uiUtils from "tns-core-modules/ui/utils";
import * as uiUtils from "../../ui/utils";
import * as utils from "../../utils/utils";
export * from "./frame-common";

View File

@@ -7,7 +7,7 @@ import { ios as iosApp } from "../../application";
import { device } from "../../platform";
// HACK: Webpack. Use a fully-qualified import to allow resolve.extensions(.ios.js) to
// kick in. `../utils` doesn't seem to trigger the webpack extensions mechanism.
import * as uiUtils from "tns-core-modules/ui/utils";
import * as uiUtils from "../../ui/utils";
import { profile } from "../../profiling";
export * from "./page-common";

View File

@@ -3,7 +3,7 @@ import { isDataURI, isFileOrResourcePath, layout, RESOURCE_PREFIX, FILE_PREFIX }
import { parse } from "../../css-value";
import { path, knownFolders } from "../../file-system";
import * as application from "../../application";
import { profile } from "tns-core-modules/profiling";
import { profile } from "../../profiling";
export * from "./background-common"
interface AndroidView {

View File

@@ -1,42 +0,0 @@
/**
* @module "ui/styling/css-selector-parser"
* @private
*/ /** */
//@private
export interface SimpleSelector {
pos: number;
type: "" | "*" | "#" | "." | ":" | "[]";
comb?: "+" | "~" | ">" | " ";
}
export interface SimpleIdentifierSelector extends SimpleSelector {
ident: string;
}
export interface UniversalSelector extends SimpleSelector {
type: "*";
}
export interface TypeSelector extends SimpleIdentifierSelector {
type: "";
}
export interface ClassSelector extends SimpleIdentifierSelector {
type: ".";
}
export interface IdSelector extends SimpleIdentifierSelector {
type: "#";
}
export interface PseudoClassSelector extends SimpleIdentifierSelector {
type: ":";
}
export interface AttributeSelector extends SimpleSelector {
type: "[]";
prop: string;
test?: "=" | "^=" | "$=" | "*=" | "=" | "~=" | "|=";
value?: string;
}
export function isUniversal(sel: SimpleSelector): sel is UniversalSelector;
export function isType(sel: SimpleSelector): sel is TypeSelector;
export function isClass(sel: SimpleSelector): sel is ClassSelector;
export function isId(sel: SimpleSelector): sel is IdSelector;
export function isPseudo(sel: SimpleSelector): sel is PseudoClassSelector;
export function isAttribute(sel: SimpleSelector): sel is AttributeSelector;
export function parse(selector: string): SimpleSelector[];

View File

@@ -1,125 +0,0 @@
/// <reference path="./css-selector-parser.d.ts" />
export interface SimpleSelector {
pos: number;
type: "" | "*" | "#" | "." | ":" | "[]";
comb?: "+" | "~" | ">" | " ";
}
export interface SimpleIdentifierSelector extends SimpleSelector {
ident: string;
}
export interface UniversalSelector extends SimpleSelector {
type: "*";
}
export interface TypeSelector extends SimpleIdentifierSelector {
type: "";
}
export interface ClassSelector extends SimpleIdentifierSelector {
type: ".";
}
export interface IdSelector extends SimpleIdentifierSelector {
type: "#";
}
export interface PseudoClassSelector extends SimpleIdentifierSelector {
type: ":";
}
export interface AttributeSelector extends SimpleSelector {
type: "[]";
prop: string;
test?: "=" | "^=" | "$=" | "*=" | "=" | "~=" | "|=";
value?: string;
}
export function isUniversal(sel: SimpleSelector): sel is UniversalSelector {
return sel.type === "*";
}
export function isType(sel: SimpleSelector): sel is TypeSelector {
return sel.type === "";
}
export function isClass(sel: SimpleSelector): sel is ClassSelector {
return sel.type === ".";
}
export function isId(sel: SimpleSelector): sel is IdSelector {
return sel.type === "#";
}
export function isPseudo(sel: SimpleSelector): sel is PseudoClassSelector {
return sel.type === ":";
}
export function isAttribute(sel: SimpleSelector): sel is AttributeSelector {
return sel.type === "[]";
}
var regex = /(\s*)(?:(\*)|(#|\.|:|\b)([_-\w][_-\w\d]*)|\[\s*([_-\w][_-\w\d]*)\s*(?:(=|\^=|\$=|\*=|\~=|\|=)\s*(?:([_-\w][_-\w\d]*)|"((?:[^\\"]|\\(?:"|n|r|f|\\|0-9a-f))*)"|'((?:[^\\']|\\(?:'|n|r|f|\\|0-9a-f))*)')\s*)?\])(?:\s*(\+|~|>|\s))?/g;
// no lead ws univ type pref and ident [ prop = ident -or- "string escapes \" \00aaff" -or- 'string escapes \' urf-8: \00aaff' ] combinator
export function parse(selector: string): SimpleSelector[] {
let selectors: any[] = [];
var result: RegExpExecArray;
var lastIndex = regex.lastIndex = 0;
while (result = regex.exec(selector)) {
let pos = result.index;
if (lastIndex !== pos) {
throw new Error(`Unexpected characters at index, near: ${lastIndex}: ${result.input.substr(lastIndex, 32)}`);
} else if (!result[0] || result[0].length === 0) {
throw new Error(`Last selector match got zero character result at index ${lastIndex}, near: ${result.input.substr(lastIndex, 32)}`);
}
pos += getLeadingWhiteSpace(result).length;
lastIndex = regex.lastIndex;
var type = getType(result);
let selector: SimpleSelector | SimpleIdentifierSelector | AttributeSelector;
switch (type) {
case "*":
selector = { pos, type };
break;
case "#":
case ".":
case ":":
case "":
let ident = getIdentifier(result);
selector = { pos, type, ident };
break;
case "[]":
let prop = getProperty(result);
let test = getPropertyTest(result);
// TODO: Unescape escape sequences. Unescape UTF-8 characters.
let value = getPropertyValue(result);
selector = test ? { pos, type, prop, test, value } : { pos, type, prop };
break;
default:
throw new Error("Unhandled type.");
}
let comb = getCombinator(result);
if (comb) {
selector.comb = comb;
}
selectors.push(selector);
}
if (selectors.length > 0) {
delete selectors[selectors.length - 1].comb;
}
return selectors;
}
function getLeadingWhiteSpace(result: RegExpExecArray): string {
return result[1] || "";
}
function getType(result: RegExpExecArray): "" | "*" | "." | "#" | ":" | "[]" {
return <"[]">(result[5] && "[]") || <"*">result[2] || <"" | "." | "#" | ":">result[3];
}
function getIdentifier(result: RegExpExecArray): string {
return result[4];
}
function getProperty(result: RegExpExecArray): string {
return result[5];
}
function getPropertyTest(result: RegExpExecArray): string {
return result[6] || undefined;
}
function getPropertyValue(result: RegExpExecArray): string {
return result[7] || result[8] || result[9];
}
function getCombinator(result: RegExpExecArray): "+" | "~" | ">" | " " {
return <("+" | "~" | ">" | " ")>result[result.length - 1] || undefined;
}

View File

@@ -1,9 +1,9 @@
import { Node, Declaration, Changes, ChangeMap } from ".";
import { isNullOrUndefined } from "../../../utils/types";
import { escapeRegexSymbols } from "../../../utils/utils";
import { escapeRegexSymbols } from "../../../utils/utils-common";
import * as cssParser from "../../../css";
import * as selectorParser from "../css-selector-parser";
import * as parser from "../../../css/parser";
const enum Specificity {
Inline = 0x01000000,
@@ -60,7 +60,7 @@ function SelectorProperties(specificity: Specificity, rarity: Rarity, dynamic: b
return cls => {
cls.prototype.specificity = specificity;
cls.prototype.rarity = rarity;
cls.prototype.combinator = "";
cls.prototype.combinator = undefined;
cls.prototype.dynamic = dynamic;
return cls;
}
@@ -408,61 +408,55 @@ function createDeclaration(decl: cssParser.Declaration): any {
return { property: decl.property.toLowerCase(), value: decl.value };
}
export function createSelector(sel: string): SimpleSelector | SimpleSelectorSequence | Selector {
try {
let ast = selectorParser.parse(sel);
if (ast.length === 0) {
return new InvalidSelector(new Error("Empty selector"));
}
let selectors = ast.map(createSimpleSelector);
let sequences: (SimpleSelector | SimpleSelectorSequence)[] = [];
// Join simple selectors into sequences, set combinators
for (let seqStart = 0, seqEnd = 0, last = selectors.length - 1; seqEnd <= last; seqEnd++) {
let sel = selectors[seqEnd];
let astComb = ast[seqEnd].comb;
if (astComb || seqEnd === last) {
if (seqStart === seqEnd) {
// This is a sequnce with single SimpleSelector, so we will not combine it into SimpleSelectorSequence.
sel.combinator = astComb;
sequences.push(sel);
} else {
let sequence = new SimpleSelectorSequence(selectors.slice(seqStart, seqEnd + 1));
sequence.combinator = astComb;
sequences.push(sequence);
}
seqStart = seqEnd + 1;
}
}
if (sequences.length === 1) {
// This is a selector with a single SinmpleSelectorSequence so we will not combine it into Selector.
return sequences[0];
} else {
return new Selector(sequences);
}
} catch(e) {
return new InvalidSelector(e);
function createSimpleSelectorFromAst(ast: parser.SimpleSelector): SimpleSelector {
switch(ast.type) {
case "*": return new UniversalSelector();
case "#": return new IdSelector(ast.identifier);
case "": return new TypeSelector(ast.identifier.replace(/-/, '').toLowerCase());
case ".": return new ClassSelector(ast.identifier);
case ":": return new PseudoClassSelector(ast.identifier);
case "[]": return ast.test ? new AttributeSelector(ast.property, ast.test, ast.value) : new AttributeSelector(ast.property);
}
}
function createSimpleSelector(sel: selectorParser.SimpleSelector): SimpleSelector {
if (selectorParser.isUniversal(sel)) {
return new UniversalSelector();
} else if (selectorParser.isId(sel)) {
return new IdSelector(sel.ident);
} else if (selectorParser.isType(sel)) {
return new TypeSelector(sel.ident.replace(/-/, '').toLowerCase());
} else if (selectorParser.isClass(sel)) {
return new ClassSelector(sel.ident);
} else if (selectorParser.isPseudo(sel)) {
return new PseudoClassSelector(sel.ident);
} else if (selectorParser.isAttribute(sel)) {
if (sel.test) {
return new AttributeSelector(sel.prop, sel.test, sel.value);
} else {
return new AttributeSelector(sel.prop)
function createSimpleSelectorSequenceFromAst(ast: parser.SimpleSelectorSequence): SimpleSelectorSequence | SimpleSelector {
if (ast.length === 0) {
return new InvalidSelector(new Error("Empty simple selector sequence."));
} else if (ast.length === 1) {
return createSimpleSelectorFromAst(ast[0]);
} else {
return new SimpleSelectorSequence(ast.map(createSimpleSelectorFromAst));
}
}
function createSelectorFromAst(ast: parser.Selector): SimpleSelector | SimpleSelectorSequence | Selector {
if (ast.length === 0) {
return new InvalidSelector(new Error("Empty selector."));
} else if (ast.length <= 2) {
return createSimpleSelectorSequenceFromAst(ast[0]);
} else {
let simpleSelectorSequences = [];
for (var i = 0; i < ast.length; i += 2) {
const simpleSelectorSequence = createSimpleSelectorSequenceFromAst(<parser.SimpleSelectorSequence>ast[i]);
const combinator = <parser.Combinator>ast[i + 1];
if (combinator) {
simpleSelectorSequence.combinator = combinator;
}
simpleSelectorSequences.push(simpleSelectorSequence);
}
return new Selector(simpleSelectorSequences);
}
}
export function createSelector(sel: string): SimpleSelector | SimpleSelectorSequence | Selector {
try {
let parsedSelector = parser.parseSelector(sel);
if (!parsedSelector) {
return new InvalidSelector(new Error("Empty selector"));
}
return createSelectorFromAst(parsedSelector.value);
} catch(e) {
return new InvalidSelector(e);
}
}

View File

@@ -1,7 +1,7 @@
import { Font as FontDefinition, ParsedFont } from "./font";
import { makeValidator, makeParser } from "../core/properties";
export abstract class FontBase implements FontDefinition {
export abstract class Font implements FontDefinition {
public static default = undefined;
get isItalic(): boolean {
@@ -23,14 +23,14 @@ export abstract class FontBase implements FontDefinition {
public readonly fontWeight: FontWeight) {
}
public abstract getAndroidTypeface(): android.graphics.Typeface;
public abstract getUIFont(defaultFont: UIFont): UIFont;
public abstract withFontFamily(family: string): FontBase;
public abstract withFontStyle(style: string): FontBase;
public abstract withFontWeight(weight: string): FontBase;
public abstract withFontSize(size: number): FontBase;
public abstract getAndroidTypeface(): any /* android.graphics.Typeface */;
public abstract getUIFont(defaultFont: any /* UIFont */): any /* UIFont */;
public abstract withFontFamily(family: string): Font;
public abstract withFontStyle(style: string): Font;
public abstract withFontWeight(weight: string): Font;
public abstract withFontSize(size: number): Font;
public static equals(value1: FontBase, value2: FontBase): boolean {
public static equals(value1: Font, value2: Font): boolean {
// both values are falsy
if (!value1 && !value2) {
return true;

View File

@@ -1,4 +1,4 @@
import { FontBase, parseFontFamily, genericFontFamilies, FontWeight } from "./font-common";
import { Font as FontBase, parseFontFamily, genericFontFamilies, FontWeight } from "./font-common";
import { isEnabled as traceEnabled, write as traceWrite, categories as traceCategories, messageType as traceMessageType } from "../../trace";
import * as application from "../../application";
import * as fs from "../../file-system";

View File

@@ -1,4 +1,4 @@
import { FontBase, parseFontFamily, genericFontFamilies, FontStyle, FontWeight } from "./font-common";
import { Font as FontBase, parseFontFamily, genericFontFamilies, FontStyle, FontWeight } from "./font-common";
import { isEnabled as traceEnabled, write as traceWrite, categories as traceCategories, messageType as traceMessageType } from "../../trace";
import { device } from "../../platform"
import * as fs from "../../file-system";

View File

@@ -8,9 +8,9 @@ import {
import { dip, px, percent } from "../core/view";
import { Color } from "../../color";
import { Font, parseFont, FontStyle, FontWeight } from "./font";
import { Font, parseFont, FontStyle, FontWeight } from "../../ui/styling/font";
import { layout } from "../../utils/utils";
import { Background } from "./background";
import { Background } from "../../ui/styling/background";
import { isIOS } from "../../platform";
import { Style } from "./style";

View File

@@ -8,6 +8,11 @@ import {
parse as parseCss,
Node as CssNode,
} from "../../css";
import {
CSS3Parser,
CSSNativeScript
} from "../../css/parser";
import {
RuleSet,
SelectorsMap,
@@ -42,6 +47,16 @@ function ensureCssAnimationParserModule() {
}
}
let parser: "rework" | "nativescript" = "rework";
try {
const appConfig = require("~/package.json");
if (appConfig && appConfig.cssParser === "nativescript") {
parser = "nativescript";
}
} catch(e) {
//
}
export function mergeCssSelectors(): void {
applicationCssSelectors = applicationSelectors.slice();
applicationCssSelectors.push.apply(applicationCssSelectors, applicationAdditionalSelectors);
@@ -58,25 +73,49 @@ const pattern: RegExp = /('|")(.*?)\1/;
class CSSSource {
private _selectors: RuleSet[] = [];
private _ast: SyntaxTree;
private static cssFilesCache: { [path: string]: CSSSource } = {};
private constructor(private _url: string, private _file: string, private _keyframes: KeyframesMap, private _source?: string) {
if (this._file && !this._source) {
this.load();
}
private constructor(private _ast: SyntaxTree, private _url: string, private _file: string, private _keyframes: KeyframesMap, private _source: string) {
this.parse();
}
public static fromURI(uri: string, keyframes: KeyframesMap): CSSSource {
try {
const cssOrAst = global.loadModule(uri);
if (cssOrAst) {
if (typeof cssOrAst === "string") {
return CSSSource.fromSource(cssOrAst, keyframes, uri);
} else if (typeof cssOrAst === "object" && cssOrAst.type === "stylesheet" && cssOrAst.stylesheet && cssOrAst.stylesheet.rules) {
return CSSSource.fromAST(cssOrAst, keyframes, uri);
} else {
// Probably a webpack css-loader exported object.
return CSSSource.fromSource(cssOrAst.toString(), keyframes, uri);
}
}
} catch(e) {
//
}
return CSSSource.fromFile(uri, keyframes);
}
public static fromFile(url: string, keyframes: KeyframesMap): CSSSource {
const file = CSSSource.resolveCSSPathFromURL(url);
return new CSSSource(undefined, url, file, keyframes, undefined);
}
@profile
public static resolveCSSPathFromURL(url: string): string {
const app = knownFolders.currentApp().path;
const file = resolveFileNameFromUrl(url, app, File.exists);
return new CSSSource(url, file, keyframes, undefined);
return file;
}
public static fromSource(source: string, keyframes: KeyframesMap, url?: string): CSSSource {
return new CSSSource(url, undefined, keyframes, source);
return new CSSSource(undefined, url, undefined, keyframes, source);
}
public static fromAST(ast: SyntaxTree, keyframes: KeyframesMap, url?: string): CSSSource {
return new CSSSource(ast, url, undefined, keyframes, undefined);
}
get selectors(): RuleSet[] { return this._selectors; }
@@ -90,24 +129,53 @@ class CSSSource {
@profile
private parse(): void {
if (this._source) {
try {
this._ast = this._source ? parseCss(this._source, { source: this._file }) : null;
// TODO: Don't merge arrays, instead chain the css files.
if (this._ast) {
this._selectors = [
...this.createSelectorsFromImports(),
...this.createSelectorsFromSyntaxTree()
];
try {
if (!this._ast) {
if (!this._source && this._file) {
this.load();
}
if (this._source) {
this.parseCSSAst();
}
} catch (e) {
traceWrite("Css styling failed: " + e, traceCategories.Error, traceMessageType.error);
}
} else {
if (this._ast) {
this.createSelectors();
} else {
this._selectors = [];
}
} catch (e) {
traceWrite("Css styling failed: " + e, traceCategories.Error, traceMessageType.error);
this._selectors = [];
}
}
@profile
private parseCSSAst() {
if (this._source) {
switch(parser) {
case "nativescript":
const cssparser = new CSS3Parser(this._source);
const stylesheet = cssparser.parseAStylesheet();
const cssNS = new CSSNativeScript();
this._ast = cssNS.parseStylesheet(stylesheet);
return;
case "rework":
this._ast = parseCss(this._source, { source: this._file });
return;
}
}
}
@profile
private createSelectors() {
if (this._ast) {
this._selectors = [
...this.createSelectorsFromImports(),
...this.createSelectorsFromSyntaxTree()
];
}
}
private createSelectorsFromImports(): RuleSet[] {
let selectors: RuleSet[] = [];
const imports = this._ast["stylesheet"]["rules"].filter(r => r.type === "import");
@@ -118,7 +186,7 @@ class CSSSource {
const url = match && match[2];
if (url !== null && url !== undefined) {
const cssFile = CSSSource.fromFile(url, this._keyframes);
const cssFile = CSSSource.fromURI(url, this._keyframes);
selectors = selectors.concat(cssFile.selectors);
}
}
@@ -169,7 +237,7 @@ const loadCss = profile(`"style-scope".loadCss`, (cssFile: string) => {
return undefined;
}
const result = CSSSource.fromFile(cssFile, applicationKeyframes).selectors;
const result = CSSSource.fromURI(cssFile, applicationKeyframes).selectors;
if (result.length > 0) {
applicationSelectors = result;
mergeCssSelectors();
@@ -179,15 +247,15 @@ const loadCss = profile(`"style-scope".loadCss`, (cssFile: string) => {
application.on("cssChanged", onCssChanged);
application.on("livesync", onLiveSync);
export const loadCssOnLaunch = profile('"style-scope".loadCssOnLaunch', () => {
loadCss(application.getCssFileName());
application.off("launch", loadCssOnLaunch);
export const loadAppCSS = profile('"style-scope".loadAppCSS', (args: application.LoadAppCSSEventData) => {
loadCss(args.cssFile);
application.off("loadAppCss", loadAppCSS);
});
if (application.hasLaunched()) {
loadCssOnLaunch();
loadAppCSS({ eventName: "loadAppCss", object: <any>application, cssFile: application.getCssFileName() });
} else {
application.on("launch", loadCssOnLaunch);
application.on("loadAppCss", loadAppCSS);
}
export class CssState {

View File

@@ -62,6 +62,7 @@ export class Style extends Observable {
public tintColor: Color;
public placeholderColor: Color;
public background: string | Color;
public backgroundColor: Color;
public backgroundImage: string;
public backgroundRepeat: BackgroundRepeat;

View File

@@ -35,6 +35,7 @@ export class Style extends Observable implements StyleDefinition {
public tintColor: Color;
public placeholderColor: Color;
public background: string | Color;
public backgroundColor: Color;
public backgroundImage: string;
public backgroundRepeat: BackgroundRepeat;

View File

@@ -1,22 +0,0 @@
import { Source } from "./debug-common";
export * from "./debug-common";
export class ScopeError extends Error {
constructor(inner: Error, message?: string) {
let formattedMessage;
if (message && inner.message) {
formattedMessage = message + "\n > " + inner.message.replace("\n", "\n ");
} else {
formattedMessage = message || inner.message || undefined;
}
super(formattedMessage);
this.stack = "Error: " + this.message + "\n" + inner.stack.substr(inner.stack.indexOf("\n") + 1);
this.message = formattedMessage;
}
}
export class SourceError extends ScopeError {
constructor(child: Error, source: Source, message?: string) {
super(child, message ? message + " @" + source + "" : source + "");
}
}

View File

@@ -1,22 +0,0 @@
import { Source } from "./debug-common";
export * from "./debug-common";
export class ScopeError extends Error {
constructor(inner: Error, message?: string) {
let formattedMessage;
if (message && inner.message) {
formattedMessage = message + "\n > " + inner.message.replace("\n", "\n ");
} else {
formattedMessage = message || inner.message || undefined;
}
super(formattedMessage);
this.stack = inner.stack;
this.message = formattedMessage;
}
}
export class SourceError extends ScopeError {
constructor(child: Error, source: Source, message?: string) {
super(child, message ? message + " @" + source + "" : source + "");
}
}

View File

@@ -1,4 +1,5 @@
import { knownFolders } from "../file-system"
import { isAndroid } from "../platform"
export var debug = true;
@@ -45,3 +46,23 @@ export class Source {
object[Source._source] = src;
}
}
export class ScopeError extends Error {
constructor(inner: Error, message?: string) {
let formattedMessage;
if (message && inner.message) {
formattedMessage = message + "\n > " + inner.message.replace("\n", "\n ");
} else {
formattedMessage = message || inner.message || undefined;
}
super(formattedMessage);
this.stack = isAndroid ? "Error: " + this.message + "\n" + inner.stack.substr(inner.stack.indexOf("\n") + 1) : inner.stack;
this.message = formattedMessage;
}
}
export class SourceError extends ScopeError {
constructor(child: Error, source: Source, message?: string) {
super(child, message ? message + " @" + source + "" : source + "");
}
}