| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1409,50 +1409,72 @@ export function test_CascadingClassNamesAppliesAfterPageLoad() { | |||
| 1409 | 1409 | } | |
| 1410 | 1410 | ||
| 1411 | 1411 | export function test_resolveFileNameFromUrl_local_file_tilda() { | |
| 1412 | - const localFileExistsMock = (fileName: string ) => true; | ||
| 1413 | - let url = "~/theme/core.css"; | ||
| 1414 | - let appDirectory = "app"; | ||
| 1415 | - let expected = `${appDirectory}/theme/core.css`; | ||
| 1416 | - let result = resolveFileNameFromUrl(url, appDirectory, localFileExistsMock); | ||
| 1412 | + const localFileExistsMock = (fileName: string) => true; | ||
| 1413 | + const url = "~/theme/core.css"; | ||
| 1414 | + const appDirectory = "app"; | ||
| 1415 | + const expected = `${appDirectory}/theme/core.css`; | ||
| 1416 | + const result = resolveFileNameFromUrl(url, appDirectory, localFileExistsMock); | ||
| 1417 | 1417 | ||
| 1418 | 1418 | TKUnit.assertEqual(result, expected, "Should resolve local file with leading tilda (~/)"); | |
| 1419 | 1419 | } | |
| 1420 | 1420 | ||
| 1421 | 1421 | export function test_resolveFileNameFromUrl_local_file_no_tilda() { | |
| 1422 | - const localFileExistsMock = (fileName: string ) => true; | ||
| 1423 | - let url = "theme/core.css"; | ||
| 1424 | - let appDirectory = "app"; | ||
| 1425 | - let expected = `${appDirectory}/theme/core.css`; | ||
| 1426 | - let result = resolveFileNameFromUrl(url, appDirectory, localFileExistsMock); | ||
| 1422 | + const localFileExistsMock = (fileName: string) => true; | ||
| 1423 | + const url = "theme/core.css"; | ||
| 1424 | + const appDirectory = "app"; | ||
| 1425 | + const expected = `${appDirectory}/theme/core.css`; | ||
| 1426 | + const result = resolveFileNameFromUrl(url, appDirectory, localFileExistsMock); | ||
| 1427 | 1427 | ||
| 1428 | 1428 | TKUnit.assertEqual(result, expected, "Should resolve local file without leading tilda (no ~/)"); | |
| 1429 | 1429 | } | |
| 1430 | 1430 | ||
| 1431 | 1431 | export function test_resolveFileNameFromUrl_external_file_tilda() { | |
| 1432 | 1432 | const externalFileExistsMock = (fileName: string) => (fileName.indexOf("tns_modules") !== -1); | |
| 1433 | - let url = "~/theme/core.css"; | ||
| 1434 | - let appDirectory = "app"; | ||
| 1435 | - let expected = `${appDirectory}/tns_modules/theme/core.css`; | ||
| 1436 | - let result = resolveFileNameFromUrl(url, appDirectory, externalFileExistsMock); | ||
| 1433 | + const url = "~/theme/core.css"; | ||
| 1434 | + const appDirectory = "app"; | ||
| 1435 | + const expected = `${appDirectory}/tns_modules/theme/core.css`; | ||
| 1436 | + const result = resolveFileNameFromUrl(url, appDirectory, externalFileExistsMock); | ||
| 1437 | 1437 | ||
| 1438 | 1438 | TKUnit.assertEqual(result, expected, "Should resolve file from tns_modules with leading tilda (~/)"); | |
| 1439 | 1439 | } | |
| 1440 | 1440 | ||
| 1441 | 1441 | export function test_resolveFileNameFromUrl_external_file_no_tilda() { | |
| 1442 | 1442 | const externalFileExistsMock = (fileName: string) => (fileName.indexOf("tns_modules") !== -1); | |
| 1443 | - let url = "theme/core.css"; | ||
| 1444 | - let appDirectory = "app"; | ||
| 1445 | - let expected = `${appDirectory}/tns_modules/theme/core.css`; | ||
| 1446 | - let result = resolveFileNameFromUrl(url, appDirectory, externalFileExistsMock); | ||
| 1443 | + const url = "theme/core.css"; | ||
| 1444 | + const appDirectory = "app"; | ||
| 1445 | + const expected = `${appDirectory}/tns_modules/theme/core.css`; | ||
| 1446 | + const result = resolveFileNameFromUrl(url, appDirectory, externalFileExistsMock); | ||
| 1447 | 1447 | ||
| 1448 | 1448 | TKUnit.assertEqual(result, expected, "Should resolve file from tns_modules without leading tilda (no ~/)"); | |
| 1449 | 1449 | } | |
| 1450 | 1450 | ||
| 1451 | + export function test_resolveFileNameFromUrl_import_relative_file_curent_dir() { | ||
| 1452 | + const importedFileExistsMock = (fileName: string) => (fileName.indexOf("views") !== -1); | ||
| 1453 | + const importSource = "app/views/main-page.css"; | ||
| 1454 | + const url = "./common.css"; | ||
| 1455 | + const appDirectory = "app"; | ||
| 1456 | + const expected = `${appDirectory}/views/common.css`; | ||
| 1457 | + const result = resolveFileNameFromUrl(url, appDirectory, importedFileExistsMock, importSource); | ||
| 1458 | + | ||
| 1459 | + TKUnit.assertEqual(result, expected, "Should resolve relative file to current directory"); | ||
| 1460 | + } | ||
| 1461 | + | ||
| 1462 | + export function test_resolveFileNameFromUrl_import_relative_file_parent_dir() { | ||
| 1463 | + const importedFileExistsMock = (fileName: string) => (fileName.indexOf("views") !== -1); | ||
| 1464 | + const importSource = "app/views/shared/main-page.css"; | ||
| 1465 | + const url = "../common.css"; | ||
| 1466 | + const appDirectory = "app"; | ||
| 1467 | + const expected = `${appDirectory}/views/common.css`; | ||
| 1468 | + const result = resolveFileNameFromUrl(url, appDirectory, importedFileExistsMock, importSource); | ||
| 1469 | + | ||
| 1470 | + TKUnit.assertEqual(result, expected, "Should resolve relative file to parent directory"); | ||
| 1471 | + } | ||
| 1472 | + | ||
| 1451 | 1473 | export function test_resolveFileNameFromUrl_unexisting_file() { | |
| 1452 | 1474 | const fileDoesNotExistMock = (fileName: string) => false; | |
| 1453 | - let url = "~/theme/core.css"; | ||
| 1454 | - let appDirectory = "app"; | ||
| 1455 | - let result = resolveFileNameFromUrl(url, appDirectory, fileDoesNotExistMock); | ||
| 1475 | + const url = "~/theme/core.css"; | ||
| 1476 | + const appDirectory = "app"; | ||
| 1477 | + const result = resolveFileNameFromUrl(url, appDirectory, fileDoesNotExistMock); | ||
| 1456 | 1478 | ||
| 1457 | 1479 | TKUnit.assertNull(result, "Shouldn't resolve unexisting file"); | |
| 1458 | 1480 | } | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -36,5 +36,5 @@ export class StyleScope { | |||
| 36 | 36 | public getAnimations(ruleset: RuleSet): KeyframeAnimationInfo[]; | |
| 37 | 37 | } | |
| 38 | 38 | ||
| 39 | - export function resolveFileNameFromUrl(url: string, appDirectory: string, fileExists: (string) => boolean): string; | ||
| 39 | + export function resolveFileNameFromUrl(url: string, appDirectory: string, fileExists: (string) => boolean, importSource?: string): string; | ||
| 40 | 40 | export function applyInlineStyle(view: ViewBase, style: string): void; | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -79,14 +79,8 @@ class CSSSource { | |||
| 79 | 79 | } | |
| 80 | 80 | ||
| 81 | 81 | public static fromURI(uri: string, keyframes: KeyframesMap): CSSSource { | |
| 82 | - // webpack modules require all file paths to be relative to /app folder. | ||
| 83 | - let appRelativeUri = uri; | ||
| 84 | - if (appRelativeUri.startsWith("/")) { | ||
| 85 | - var app = knownFolders.currentApp().path + "/"; | ||
| 86 | - if (appRelativeUri.startsWith(app)) { | ||
| 87 | - appRelativeUri = "./" + appRelativeUri.substr(app.length); | ||
| 88 | - } | ||
| 89 | - } | ||
| 82 | + // webpack modules require all file paths to be relative to /app folder | ||
| 83 | + let appRelativeUri = CSSSource.pathRelativeToApp(uri); | ||
| 90 | 84 | ||
| 91 | 85 | try { | |
| 92 | 86 | const cssOrAst = global.loadModule(appRelativeUri); | |
@@ -109,6 +103,21 @@ class CSSSource { | |||
| 109 | 103 | return CSSSource.fromFile(appRelativeUri, keyframes); | |
| 110 | 104 | } | |
| 111 | 105 | ||
| 106 | + private static pathRelativeToApp(uri: string): string { | ||
| 107 | + if (!uri.startsWith("/")) { | ||
| 108 | + return uri; | ||
| 109 | + } | ||
| 110 | + | ||
| 111 | + const appPath = knownFolders.currentApp().path; | ||
| 112 | + if (!uri.startsWith(appPath)) { | ||
| 113 | + traceWrite(`${uri} does not start with ${appPath}`, traceCategories.Error, traceMessageType.error); | ||
| 114 | + return uri; | ||
| 115 | + } | ||
| 116 | + | ||
| 117 | + const relativeUri = `.${uri.substr(appPath.length)}`; | ||
| 118 | + return relativeUri; | ||
| 119 | + } | ||
| 120 | + | ||
| 112 | 121 | public static fromFile(url: string, keyframes: KeyframesMap): CSSSource { | |
| 113 | 122 | // .scss, .sass, etc. css files in vanilla app are usually compiled to .css so we will try to load a compiled file first. | |
| 114 | 123 | let cssFileUrl = url.replace(/\..\w+$/, ".css"); | |
@@ -123,10 +132,15 @@ class CSSSource { | |||
| 123 | 132 | return new CSSSource(undefined, url, file, keyframes, undefined); | |
| 124 | 133 | } | |
| 125 | 134 | ||
| 135 | + public static fromFileImport(url: string, keyframes: KeyframesMap, importSource: string): CSSSource { | ||
| 136 | + const file = CSSSource.resolveCSSPathFromURL(url, importSource); | ||
| 137 | + return new CSSSource(undefined, url, file, keyframes, undefined); | ||
| 138 | + } | ||
| 139 | + | ||
| 126 | 140 | @profile | |
| 127 | - public static resolveCSSPathFromURL(url: string): string { | ||
| 141 | + public static resolveCSSPathFromURL(url: string, importSource?: string): string { | ||
| 128 | 142 | const app = knownFolders.currentApp().path; | |
| 129 | - const file = resolveFileNameFromUrl(url, app, File.exists); | ||
| 143 | + const file = resolveFileNameFromUrl(url, app, File.exists, importSource); | ||
| 130 | 144 | return file; | |
| 131 | 145 | } | |
| 132 | 146 | ||
@@ -197,21 +211,33 @@ class CSSSource { | |||
| 197 | 211 | } | |
| 198 | 212 | ||
| 199 | 213 | private createSelectorsFromImports(): RuleSet[] { | |
| 200 | - let selectors: RuleSet[] = []; | ||
| 201 | 214 | const imports = this._ast["stylesheet"]["rules"].filter(r => r.type === "import"); | |
| 202 | - for (let i = 0; i < imports.length; i++) { | ||
| 203 | - const importItem = imports[i]["import"]; | ||
| 204 | 215 | ||
| 205 | - const match = importItem && (<string>importItem).match(pattern); | ||
| 206 | - const url = match && match[2]; | ||
| 216 | + const urlFromImportObject = importObject => { | ||
| 217 | + const importItem = importObject["import"] as string; | ||
| 218 | + const urlMatch = importItem && importItem.match(pattern); | ||
| 219 | + return urlMatch && urlMatch[2]; | ||
| 220 | + }; | ||
| 207 | 221 | ||
| 208 | - if (url !== null && url !== undefined) { | ||
| 209 | - const cssFile = CSSSource.fromURI(url, this._keyframes); | ||
| 210 | - selectors = selectors.concat(cssFile.selectors); | ||
| 211 | - } | ||
| 212 | - } | ||
| 222 | + const sourceFromImportObject = importObject => | ||
| 223 | + importObject["position"] && importObject["position"]["source"]; | ||
| 224 | + | ||
| 225 | + const toUrlSourcePair = importObject => ({ | ||
| 226 | + url: urlFromImportObject(importObject), | ||
| 227 | + source: sourceFromImportObject(importObject), | ||
| 228 | + }); | ||
| 213 | 229 | ||
| 214 | - return selectors; | ||
| 230 | + const getCssFile = ({ url, source }) => source ? | ||
| 231 | + CSSSource.fromFileImport(url, this._keyframes, source) : | ||
| 232 | + CSSSource.fromURI(url, this._keyframes); | ||
| 233 | + | ||
| 234 | + const cssFiles = imports | ||
| 235 | + .map(toUrlSourcePair) | ||
| 236 | + .filter(({ url }) => !!url) | ||
| 237 | + .map(getCssFile); | ||
| 238 | + | ||
| 239 | + const selectors = cssFiles.map(file => (file && file.selectors) || []); | ||
| 240 | + return selectors.reduce((acc, val) => acc.concat(val), []); | ||
| 215 | 241 | } | |
| 216 | 242 | ||
| 217 | 243 | private createSelectorsFromSyntaxTree(): RuleSet[] { | |
@@ -521,13 +547,13 @@ export class StyleScope { | |||
| 521 | 547 | ||
| 522 | 548 | let parsedCssSelectors = cssString ? CSSSource.fromSource(cssString, this._keyframes, cssFileName) : CSSSource.fromURI(cssFileName, this._keyframes); | |
| 523 | 549 | this._css = this._css + parsedCssSelectors.source; | |
| 524 | - this._localCssSelectors.push.apply(this._localCssSelectors, parsedCssSelectors.selectors); | ||
| 550 | + this._localCssSelectors.push.apply(this._localCssSelectors, parsedCssSelectors.selectors); | ||
| 525 | 551 | this._localCssSelectorVersion++; | |
| 526 | 552 | this.ensureSelectors(); | |
| 527 | 553 | } | |
| 528 | 554 | ||
| 529 | 555 | public getKeyframeAnimationWithName(animationName: string): kam.KeyframeAnimationInfo { | |
| 530 | - const cssKeyframes = this._keyframes[animationName]; | ||
| 556 | + const cssKeyframes = this._keyframes[animationName]; | ||
| 531 | 557 | if (!cssKeyframes) { | |
| 532 | 558 | return; | |
| 533 | 559 | } | |
@@ -611,9 +637,9 @@ export class StyleScope { | |||
| 611 | 637 | ||
| 612 | 638 | type KeyframesMap = Map<string, Keyframes>; | |
| 613 | 639 | ||
| 614 | - export function resolveFileNameFromUrl(url: string, appDirectory: string, fileExists: (name: string) => boolean): string { | ||
| 615 | - let fileName: string = typeof url === "string" ? url.trim() : ""; | ||
| 640 | + export function resolveFileNameFromUrl(url: string, appDirectory: string, fileExists: (name: string) => boolean, importSource?: string): string { | ||
| 616 | 641 | ||
| 642 | + let fileName: string = typeof url === "string" ? url.trim() : ""; | ||
| 617 | 643 | if (fileName.indexOf("~/") === 0) { | |
| 618 | 644 | fileName = fileName.replace("~/", ""); | |
| 619 | 645 | } | |
@@ -628,6 +654,14 @@ export function resolveFileNameFromUrl(url: string, appDirectory: string, fileEx | |||
| 628 | 654 | if (fileName[0] === "~" && fileName[1] !== "/" && fileName[1] !== "\"") { | |
| 629 | 655 | fileName = fileName.substr(1); | |
| 630 | 656 | } | |
| 657 | + | ||
| 658 | + if (importSource) { | ||
| 659 | + const importFile = resolveFilePathFromImport(importSource, fileName); | ||
| 660 | + if (fileExists(importFile)) { | ||
| 661 | + return importFile; | ||
| 662 | + } | ||
| 663 | + } | ||
| 664 | + | ||
| 631 | 665 | const external = path.join(appDirectory, "tns_modules", fileName); | |
| 632 | 666 | if (fileExists(external)) { | |
| 633 | 667 | return external; | |
@@ -637,6 +671,19 @@ export function resolveFileNameFromUrl(url: string, appDirectory: string, fileEx | |||
| 637 | 671 | return null; | |
| 638 | 672 | } | |
| 639 | 673 | ||
| 674 | + function resolveFilePathFromImport(importSource: string, fileName: string): string { | ||
| 675 | + const importSourceParts = importSource.split(path.separator); | ||
| 676 | + const fileNameParts = fileName.split(path.separator) | ||
| 677 | + // exclude the dot-segment for current directory | ||
| 678 | + .filter(p => !isCurrentDirectory(p)); | ||
| 679 | + | ||
| 680 | + // remove current file name | ||
| 681 | + importSourceParts.pop(); | ||
| 682 | + // remove element in case of dot-segment for parent directory or add file name | ||
| 683 | + fileNameParts.forEach(p => isParentDirectory(p) ? importSourceParts.pop() : importSourceParts.push(p)); | ||
| 684 | + return importSourceParts.join(path.separator); | ||
| 685 | + } | ||
| 686 | + | ||
| 640 | 687 | export const applyInlineStyle = profile(function applyInlineStyle(view: ViewBase, styleStr: string) { | |
| 641 | 688 | let localStyle = `local { ${styleStr} }`; | |
| 642 | 689 | let inlineRuleSet = CSSSource.fromSource(localStyle, new Map()).selectors; | |
@@ -657,6 +704,14 @@ export const applyInlineStyle = profile(function applyInlineStyle(view: ViewBase | |||
| 657 | 704 | }); | |
| 658 | 705 | }); | |
| 659 | 706 | ||
| 707 | + function isCurrentDirectory(uriPart: string): boolean { | ||
| 708 | + return uriPart === "."; | ||
| 709 | + } | ||
| 710 | + | ||
| 711 | + function isParentDirectory(uriPart: string): boolean { | ||
| 712 | + return uriPart === ".."; | ||
| 713 | + } | ||
| 714 | + | ||
| 660 | 715 | function isKeyframe(node: CssNode): node is KeyframesDefinition { | |
| 661 | 716 | return node.type === "keyframes"; | |
| 662 | 717 | } | |
| Back | FazBrowse Home | New Git URL |
0 commit comments