-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
80859f5
commit 056af8a
Showing
7 changed files
with
249 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
/** | ||
* @typedef {import('@glimmer/syntax').ASTPlugin} ASTPlugin | ||
* @typedef {import('@glimmer/syntax').ASTPluginEnvironment} ASTPluginEnvironment | ||
* | ||
*/ | ||
|
||
import { getCSSInfo } from '../lib/css/utils.js'; | ||
import { fixFilename } from '../lib/path/template-transform-paths.js'; | ||
import { | ||
appPath, | ||
cssPathFor, | ||
hashFromModulePath, | ||
isRelevantFile, | ||
} from '../lib/path/utils.js'; | ||
import { templatePlugin } from '../lib/rewriteHbs.js'; | ||
|
||
const noopPlugin = { | ||
name: 'ember-scoped-css:noop', | ||
visitor: {}, | ||
}; | ||
|
||
/** | ||
* @returns {ASTPlugin} | ||
*/ | ||
export function createPlugin(config) { | ||
/** | ||
* | ||
* @param {ASTPluginEnvironment} env | ||
*/ | ||
return function scopedCss(env) { | ||
let isRelevant = isRelevantFile(env.filename, config.additionalRoots); | ||
|
||
if (!isRelevant) { | ||
return noopPlugin; | ||
} | ||
|
||
let absolutePath = fixFilename(env.filename); | ||
let modulePath = appPath(absolutePath); | ||
|
||
let cssPath = cssPathFor(absolutePath); | ||
let info = getCSSInfo(cssPath); | ||
let postfix = hashFromModulePath(modulePath); | ||
|
||
if (!info) { | ||
return noopPlugin; | ||
} | ||
|
||
let visitors = templatePlugin({ | ||
classes: info.classes, | ||
tags: info.tags, | ||
postfix, | ||
}); | ||
|
||
return { | ||
name: 'ember-scoped-css:template-plugin', | ||
visitor: { | ||
// Stack Manager | ||
...visitors, | ||
// Visitors broken out like this so we can conditionally | ||
// debug based on file path. | ||
AttrNode(...args) { | ||
return visitors.AttrNode(...args); | ||
}, | ||
ElementNode(...args) { | ||
return visitors.ElementNode(...args); | ||
}, | ||
MustacheStatement(...args) { | ||
return visitors.MustacheStatement(...args); | ||
}, | ||
SubExpression(...args) { | ||
return visitors.SubExpression(...args); | ||
}, | ||
}, | ||
}; | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
import { existsSync, readFileSync } from 'fs'; | ||
|
||
import getClassesTagsFromCss from '../getClassesTagsFromCss'; | ||
|
||
/** | ||
* @param {string} cssPath path to a CSS file | ||
*/ | ||
export function getCSSInfo(cssPath) { | ||
if (!existsSync(cssPath)) { | ||
return null; | ||
} | ||
|
||
let css = readFileSync(cssPath, 'utf8'); | ||
let result = getClassesTagsFromCss(css); | ||
|
||
return result; | ||
} |
103 changes: 103 additions & 0 deletions
103
ember-scoped-css/src/lib/path/template-transform-paths.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
import path from 'node:path'; | ||
|
||
import { existsSync } from 'fs'; | ||
|
||
import { findWorkspacePath, withoutExtension } from './utils'; | ||
|
||
/** | ||
* template plugins do not hand us the correct file path. | ||
* additionally, we may not be able to rely on this data in the future, | ||
* so this functions acts as a means of normalizing _whatever_ we're given | ||
* in the future. | ||
* | ||
* @param {string} filename | ||
* @returns {string} the absolute path to the file | ||
*/ | ||
export function fixFilename(filename) { | ||
let fileName = filename; | ||
let workspace = findWorkspacePath(fileName); | ||
|
||
/** | ||
* ember-source 5.8: | ||
* - the filename looks like an absolute path, but swapped out the 'app' part of the path | ||
* with the module name, so the file paths never exist on disk | ||
*/ | ||
if (!fileName.includes('/app/')) { | ||
let maybeModule = fileName.replace(workspace, ''); | ||
let [maybeScope, ...rest] = maybeModule.split('/').filter(Boolean); | ||
let parts = rest; | ||
|
||
if (maybeScope.startsWith('@')) { | ||
let [, ...rester] = rest; | ||
|
||
parts = rester; | ||
} | ||
|
||
let relative = path.join(...parts); | ||
|
||
/** | ||
* We don't actually know if this file is an app. | ||
* it could be an addon (v1 or v2) | ||
* | ||
* So here we log to see if we have unhandled situations. | ||
*/ | ||
let candidatePath = path.join(workspace, 'app', relative); | ||
|
||
let resolved = findCandidate(candidatePath); | ||
|
||
if (resolved) { | ||
return resolved; | ||
} | ||
} | ||
|
||
/** | ||
* under embroider@3, the fileName will be the path to the rewritten file. | ||
* we don't want this. | ||
* we want the path to the original source. | ||
* Through the powers of ✨ convention ✨, we can map back to source. | ||
*/ | ||
if (fileName.includes('/node_modules/.embroider/rewritten-app/')) { | ||
let candidatePath = fileName.replace( | ||
'/node_modules/.embroider/rewritten-app/', | ||
'/app/', | ||
); | ||
|
||
let resolved = findCandidate(candidatePath); | ||
|
||
if (resolved) { | ||
return resolved; | ||
} | ||
} | ||
|
||
// TODO: why are we passed files to other projects? | ||
if (!fileName.includes(workspace)) { | ||
return fileName; | ||
} | ||
|
||
console.debug(`[ScopedCSS]: Failed to handle ${fileName}`); | ||
|
||
// Fallback to what the plugin system gives us. | ||
// This may be wrong, and if wrong, reveals | ||
// unhandled scenarios with the file names in the plugin infra | ||
return fileName; | ||
} | ||
|
||
const COMPILES_TO_JS = ['.hbs', '.gjs', '.gts']; | ||
|
||
function findCandidate(filePath) { | ||
if (existsSync(filePath)) { | ||
return filePath; | ||
} | ||
|
||
let withoutExt = withoutExtension(filePath); | ||
|
||
for (let ext of COMPILES_TO_JS) { | ||
let candidatePath = withoutExt + ext; | ||
|
||
if (existsSync(candidatePath)) { | ||
return candidatePath; | ||
} | ||
} | ||
|
||
return null; | ||
} |
47 changes: 47 additions & 0 deletions
47
ember-scoped-css/src/lib/path/template-transform-paths.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
import path from 'node:path'; | ||
|
||
import { describe, expect, it } from 'vitest'; | ||
|
||
import { fixFilename } from './template-transform-paths.js'; | ||
import { paths } from './utils.paths.test.js'; | ||
|
||
describe('fixFilename()', () => { | ||
it('works with embroider paths', () => { | ||
let file = path.join( | ||
paths.embroiderApp, | ||
paths.rewritten, | ||
'components/template-only.hbs', | ||
); | ||
let corrected = fixFilename(file); | ||
|
||
expect(corrected).to.equal( | ||
path.join(paths.embroiderApp, 'app/components/template-only.hbs'), | ||
); | ||
}); | ||
|
||
it('works with the real path', () => { | ||
let file = path.join( | ||
paths.classicApp, | ||
'app', | ||
'components/template-only.hbs', | ||
); | ||
let corrected = fixFilename(file); | ||
|
||
expect(corrected).to.equal( | ||
path.join(paths.classicApp, 'app/components/template-only.hbs'), | ||
); | ||
}); | ||
|
||
it('works with classic paths (w/ module name)', () => { | ||
let file = path.join( | ||
paths.classicApp, | ||
'classic-app', | ||
'components/template-only.hbs', | ||
); | ||
let corrected = fixFilename(file); | ||
|
||
expect(corrected).to.equal( | ||
path.join(paths.classicApp, 'app/components/template-only.hbs'), | ||
); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters