feat: support exclusive discovered tags folder as interop heuristic

This commit is contained in:
dpiercey 2025-12-11 10:22:27 -07:00 committed by Dylan Piercey
parent a228ddc526
commit d2f437bf71
37 changed files with 357 additions and 193 deletions

View File

@ -0,0 +1,6 @@
---
"marko": patch
"@marko/runtime-tags": patch
---
Add style block as tag api interop heuristic.

View File

@ -0,0 +1,7 @@
---
"marko": patch
"@marko/runtime-tags": patch
"@marko/compiler": patch
---
Add support for "use class" comment to opt into to class api when exclusive `tags` folders prefer tags api.

View File

@ -0,0 +1,7 @@
---
"marko": patch
"@marko/runtime-tags": patch
"@marko/compiler": patch
---
Add exclusive `tags` folder discovery as a "prefer tags api" heuristic in interop mode.

View File

@ -75,7 +75,6 @@ export interface TagDefinition {
}
export interface TaglibLookup {
manualTagsDirs: Set<string>;
getTagsSorted(): TagDefinition[];
getTag(tagName: string): undefined | TagDefinition;
getAttribute(
@ -86,6 +85,8 @@ export interface TaglibLookup {
tagName: string,
callback: (attr: AttributeDefinition, tag: TagDefinition) => void,
): void;
exclusiveTagDiscoveryDirs: undefined | false | string;
manualTagsDirs: undefined | Set<string>;
}
export interface Attribute {

View File

@ -51,27 +51,13 @@ function getAllDependencyNames(pkg) {
}
function find(dirname, registeredTaglibs, tagDiscoveryDirs) {
var found = findCache[dirname];
if (found) {
return found;
var cached = findCache[dirname];
if (cached) {
return cached.taglibs;
}
found = [];
var added = {};
var helper = {
addTaglib: function (taglib) {
if (added[taglib.id]) {
return;
}
added[taglib.id] = true;
found.push(taglib);
},
foundTaglibPackages: {},
};
var taglibs = [];
var added = new Set();
var rootDirname = markoModules.cwd; // Don't search up past this directory
var rootPkg = getModuleRootPackage(dirname);
if (rootPkg) {
@ -80,27 +66,41 @@ function find(dirname, registeredTaglibs, tagDiscoveryDirs) {
// First walk up the directory tree looking for marko.json files or components/ directories
let curDirname = dirname;
// exclusiveTagDiscoveryDirs is used for the interop to detect if `tags` directories are used exclusively when finding tags
let exclusiveTagDiscoveryDirs = undefined;
while (true) {
if (!excludedDirs[curDirname]) {
let taglibPath = nodePath.join(curDirname, "marko.json");
let taglib;
let manualTagsDir;
if (existsSync(taglibPath)) {
taglib = taglibLoader.loadTaglibFromFile(taglibPath);
helper.addTaglib(taglib);
manualTagsDir = taglib.tagsDir;
addTaglib(taglib);
}
if (!taglib || taglib.tagsDir === undefined) {
if (manualTagsDir === undefined) {
for (const tagDiscoveryDir of tagDiscoveryDirs) {
const componentsPath = nodePath.join(curDirname, tagDiscoveryDir);
if (existsSync(componentsPath) && !excludedDirs[componentsPath]) {
helper.addTaglib(
if (exclusiveTagDiscoveryDirs !== false) {
if (exclusiveTagDiscoveryDirs === undefined) {
exclusiveTagDiscoveryDirs = tagDiscoveryDir;
} else if (exclusiveTagDiscoveryDirs !== tagDiscoveryDir) {
exclusiveTagDiscoveryDirs = false;
}
}
addTaglib(
taglibLoader.loadTaglibFromDir(curDirname, tagDiscoveryDir),
);
}
}
} else if (manualTagsDir) {
exclusiveTagDiscoveryDirs = false;
}
}
@ -120,26 +120,41 @@ function find(dirname, registeredTaglibs, tagDiscoveryDirs) {
getAllDependencyNames(rootPkg).forEach((name) => {
if (!excludedPackages[name]) {
let taglibPath = markoModules.tryResolve(
nodePath.join(name, "marko.json"),
name + "/marko.json",
rootPkg.__dirname,
);
if (taglibPath) {
var taglib = taglibLoader.loadTaglibFromFile(taglibPath, true);
helper.addTaglib(taglib);
addTaglib(taglib);
}
}
});
}
for (let i = registeredTaglibs.length; i--; ) {
helper.addTaglib(registeredTaglibs[i]);
addTaglib(registeredTaglibs[i]);
}
findCache[dirname] = found;
findCache[dirname] = { exclusiveTagDiscoveryDirs, taglibs };
return taglibs;
return found;
function addTaglib(taglib) {
if (!added.has(taglib.id)) {
added.add(taglib.id);
taglibs.push(taglib);
}
}
}
find._withMeta = function findWithMeta(
dirname,
registeredTaglibs,
tagDiscoveryDirs,
) {
find(dirname, registeredTaglibs, tagDiscoveryDirs);
return findCache[dirname];
};
function clearCache() {
findCache = {};
}

View File

@ -32,6 +32,7 @@ export function buildLookup(dirname, requestedTranslator, onError) {
}
let taglibsForDir = loadedTranslatorsTaglibs.get(translator);
let exclusiveTagDiscoveryDirs = undefined;
if (!taglibsForDir) {
loadedTranslatorsTaglibs.set(
@ -45,11 +46,13 @@ export function buildLookup(dirname, requestedTranslator, onError) {
}
runAndCatchErrors(() => {
taglibsForDir = finder.find(
const foundMeta = finder.find._withMeta(
dirname,
taglibsForDir,
translator.tagDiscoveryDirs,
);
taglibsForDir = foundMeta.taglibs;
exclusiveTagDiscoveryDirs = foundMeta.exclusiveTagDiscoveryDirs;
}, onError);
const cacheKey = taglibsForDir
@ -60,6 +63,7 @@ export function buildLookup(dirname, requestedTranslator, onError) {
if (!lookup) {
lookup = lookupCache[cacheKey] = new Lookup();
lookup.exclusiveTagDiscoveryDirs = exclusiveTagDiscoveryDirs;
for (let i = taglibsForDir.length; i--; ) {
const taglib = taglibsForDir[i];
lookup.addTaglib(taglib);

View File

@ -39,6 +39,7 @@ class Taglib {
this.patternAttributes = [];
this.imports = null;
this.importsLookup = null;
this.tagsDir = undefined;
}
addAttribute(attribute) {

View File

@ -52,7 +52,8 @@ class TaglibLookup {
this.taglibsById = {};
this._sortedTags = undefined;
this.manualTagsDirs = new Set();
this.exclusiveTagDiscoveryDirs = undefined;
this.manualTagsDirs = undefined;
}
hasTaglib(taglib) {
@ -103,7 +104,9 @@ class TaglibLookup {
typeof taglib.tagsDir === "string" &&
/[/\\]tags[/\\]?$/.test(taglib.tagsDir)
) {
this.manualTagsDirs.add(nodePath.resolve(taglib.dirname, taglib.tagsDir));
(this.manualTagsDirs || (this.manualTagsDirs = new Set())).add(
nodePath.resolve(taglib.dirname, taglib.tagsDir),
);
}
// console.log("TAGLIB:", taglib);

View File

@ -0,0 +1,14 @@
# Render
```html
<h1>
Hello tags
</h1>
<h1>
Hello components
</h1>
```
# Mutations
```
INSERT #text0, #text1, #text2, h10, #text3, #text4, #text5, h11, #text6, #text7
```

View File

@ -0,0 +1,19 @@
import { t as _t } from "marko/src/runtime/vdom/index.js";
const _marko_componentType = "__tests__/components/hello-components.marko",
_marko_template = _t(_marko_componentType);
export default _marko_template;
import _marko_renderer from "marko/src/runtime/components/renderer.js";
import { r as _marko_registerComponent } from "marko/src/runtime/components/registry.js";
_marko_registerComponent(_marko_componentType, () => _marko_template);
const _marko_component = {};
_marko_template._ = _marko_renderer(function (input, out, _componentDef, _component, state, $global) {
out.be("h1", null, "0", _component, null, 0);
out.t("Hello components", _component);
out.ee();
}, {
t: _marko_componentType,
i: true,
d: true
}, _marko_component);
import _marko_defineComponent from "marko/src/runtime/components/defineComponent.js";
_marko_template.Component = _marko_defineComponent(_marko_component, _marko_template._);

View File

@ -0,0 +1,5 @@
export const $template = "<h1>Hello tags</h1>";
export const $walks = /* over(1) */"b";
export const $setup = () => {};
import * as _ from "@marko/runtime-tags/debug/dom";
export default /* @__PURE__ */_._template("__tests__/tags/hello-tags.marko", $template, $walks, $setup);

View File

@ -0,0 +1,4 @@
import init6 from "virtual:./template.marko.hydrate-6.js import \"./tags/hello-tags.marko\";\nexport default () => {};";
import init5 from "virtual:./template.marko.hydrate-5.js export default () => {};";
init6();
init5();

View File

@ -0,0 +1,23 @@
import "marko/src/runtime/helpers/tags-compat/dom-debug.mjs";
import { t as _t } from "marko/src/runtime/vdom/index.js";
const _marko_componentType = "__tests__/template.marko",
_marko_template = _t(_marko_componentType);
export default _marko_template;
import _helloTags from "./tags/hello-tags.marko";
import _marko_dynamic_tag from "marko/src/runtime/helpers/dynamic-tag.js";
import _helloComponents from "./components/hello-components.marko";
import _marko_tag from "marko/src/runtime/helpers/render-tag.js";
import _marko_renderer from "marko/src/runtime/components/renderer.js";
import { r as _marko_registerComponent } from "marko/src/runtime/components/registry.js";
_marko_registerComponent(_marko_componentType, () => _marko_template);
const _marko_component = {};
_marko_template._ = _marko_renderer(function (input, out, _componentDef, _component, state, $global) {
_marko_dynamic_tag(out, _helloTags, null, null, null, null, _componentDef, "0");
_marko_tag(_helloComponents, {}, out, _componentDef, "1");
}, {
t: _marko_componentType,
i: true,
d: true
}, _marko_component);
import _marko_defineComponent from "marko/src/runtime/components/defineComponent.js";
_marko_template.Component = _marko_defineComponent(_marko_component, _marko_template._);

View File

@ -0,0 +1,15 @@
import { t as _t } from "marko/src/runtime/html/index.js";
const _marko_componentType = "__tests__/components/hello-components.marko",
_marko_template = _t(_marko_componentType);
export default _marko_template;
import _marko_renderer from "marko/src/runtime/components/renderer.js";
const _marko_component = {};
_marko_template._ = _marko_renderer(function (input, out, _componentDef, _component, state, $global) {
out.w("<h1>");
out.w("Hello components");
out.w("</h1>");
}, {
t: _marko_componentType,
i: true,
d: true
}, _marko_component);

View File

@ -0,0 +1,5 @@
import * as _ from "@marko/runtime-tags/debug/html";
export default _._template("__tests__/tags/hello-tags.marko", input => {
const $scope0_id = _._scope_id();
_._html("<h1>Hello tags</h1>");
});

View File

@ -0,0 +1,19 @@
import "marko/src/runtime/helpers/tags-compat/html-debug.mjs";
import { t as _t } from "marko/src/runtime/html/index.js";
const _marko_componentType = "__tests__/template.marko",
_marko_template = _t(_marko_componentType);
export default _marko_template;
import _helloTags from "./tags/hello-tags.marko";
import _marko_dynamic_tag from "marko/src/runtime/helpers/dynamic-tag.js";
import _helloComponents from "./components/hello-components.marko";
import _marko_tag from "marko/src/runtime/helpers/render-tag.js";
import _marko_renderer from "marko/src/runtime/components/renderer.js";
const _marko_component = {};
_marko_template._ = _marko_renderer(function (input, out, _componentDef, _component, state, $global) {
_marko_dynamic_tag(out, _helloTags, null, null, null, null, _componentDef, "0");
_marko_tag(_helloComponents, {}, out, _componentDef, "1");
}, {
t: _marko_componentType,
i: true,
d: true
}, _marko_component);

View File

@ -0,0 +1,14 @@
# Render
```html
<html>
<head />
<body>
<h1>
Hello tags
</h1>
<h1>
Hello components
</h1>
</body>
</html>
```

View File

@ -0,0 +1,28 @@
# Write
<h1>Hello tags</h1><h1>Hello components</h1>
# Render End
```html
<html>
<head />
<body>
<h1>
Hello tags
</h1>
<h1>
Hello components
</h1>
</body>
</html>
```
# Mutations
```
INSERT html
INSERT html/head
INSERT html/body
INSERT html/body/h10
INSERT html/body/h10/#text
INSERT html/body/h11
INSERT html/body/h11/#text
```

View File

@ -0,0 +1,2 @@
<hello-tags/>
<hello-components/>

View File

@ -7,5 +7,5 @@
# Mutations
```
INSERT #text0, #text1, #text2, h1, #text3, #text4, #text5
INSERT h1
```

View File

@ -1,4 +1,2 @@
import init6 from "virtual:./template.marko.hydrate-6.js import \"./tags/hello.marko\";\nexport default () => {};";
import init5 from "virtual:./template.marko.hydrate-5.js export default () => {};";
init6();
init5();
import "./template.marko";
import "./tags/hello.marko";

View File

@ -1,20 +1,8 @@
import "marko/src/runtime/helpers/tags-compat/dom-debug.mjs";
import { t as _t } from "marko/src/runtime/vdom/index.js";
const _marko_componentType = "__tests__/template.marko",
_marko_template = _t(_marko_componentType);
export default _marko_template;
import _hello from "./tags/hello.marko";
import _marko_dynamic_tag from "marko/src/runtime/helpers/dynamic-tag.js";
import _marko_renderer from "marko/src/runtime/components/renderer.js";
import { r as _marko_registerComponent } from "marko/src/runtime/components/registry.js";
_marko_registerComponent(_marko_componentType, () => _marko_template);
const _marko_component = {};
_marko_template._ = _marko_renderer(function (input, out, _componentDef, _component, state, $global) {
_marko_dynamic_tag(out, _hello, null, null, null, null, _componentDef, "0");
}, {
t: _marko_componentType,
i: true,
d: true
}, _marko_component);
import _marko_defineComponent from "marko/src/runtime/components/defineComponent.js";
_marko_template.Component = _marko_defineComponent(_marko_component, _marko_template._);
export const $template = _hello_template;
export const $walks = /* <hello> */`/${_hello_walks}&`;
import { $setup as _hello, $template as _hello_template, $walks as _hello_walks } from "./tags/hello.marko";
export function $setup($scope) {
_hello($scope["#childScope/0"]);
}
import * as _ from "@marko/runtime-tags/debug/dom";
export default /* @__PURE__ */_._template("__tests__/template.marko", $template, $walks, $setup);

View File

@ -1,16 +1,6 @@
import "marko/src/runtime/helpers/tags-compat/html-debug.mjs";
import { t as _t } from "marko/src/runtime/html/index.js";
const _marko_componentType = "__tests__/template.marko",
_marko_template = _t(_marko_componentType);
export default _marko_template;
import _hello from "./tags/hello.marko";
import _marko_dynamic_tag from "marko/src/runtime/helpers/dynamic-tag.js";
import _marko_renderer from "marko/src/runtime/components/renderer.js";
const _marko_component = {};
_marko_template._ = _marko_renderer(function (input, out, _componentDef, _component, state, $global) {
_marko_dynamic_tag(out, _hello, null, null, null, null, _componentDef, "0");
}, {
t: _marko_componentType,
i: true,
d: true
}, _marko_component);
import * as _ from "@marko/runtime-tags/debug/html";
export default _._template("__tests__/template.marko", input => {
const $scope0_id = _._scope_id();
_hello({});
});

View File

@ -1,6 +1,10 @@
Cannot mix Tags API and Class API features in the same file:
at packages/runtime-tags/src/__tests__/fixtures-interop/error-class-tags-dir/tags/hello.marko:1:1
> 1 | class {}
| ^^^^^ Unable to find entry point for [custom tag](https://markojs.com/docs/reference/custom-tag#relative-custom-tags) `<class>`.
2 |
3 | <h1>Hello world</h1>
Template file within a tags directory at packages/runtime-tags/src/__tests__/fixtures-interop/error-class-tags-dir/tags/hello.marko:
<class> tag at packages/runtime-tags/src/__tests__/fixtures-interop/error-class-tags-dir/tags/hello.marko(1,1):
> 1 | class {}
| ^^^^^
2 |
3 | <h1>Hello world</h1>

View File

@ -1,19 +1,9 @@
import { t as _t } from "marko/src/runtime/vdom/index.js";
const _marko_componentType = "__tests__/template.marko",
_marko_template = _t(_marko_componentType);
export default _marko_template;
export const $template = "<!><!><!>";
export const $walks = /* over(1), replace, over(2) */"b%c";
import _hello from "./tags/hello.marko";
import _marko_tag from "marko/src/runtime/helpers/render-tag.js";
import _marko_renderer from "marko/src/runtime/components/renderer.js";
import { r as _marko_registerComponent } from "marko/src/runtime/components/registry.js";
_marko_registerComponent(_marko_componentType, () => _marko_template);
const _marko_component = {};
_marko_template._ = _marko_renderer(function (input, out, _componentDef, _component, state, $global) {
_marko_tag(_hello, {}, out, _componentDef, "0");
}, {
t: _marko_componentType,
i: true,
d: true
}, _marko_component);
import _marko_defineComponent from "marko/src/runtime/components/defineComponent.js";
_marko_template.Component = _marko_defineComponent(_marko_component, _marko_template._);
import * as _ from "@marko/runtime-tags/debug/dom";
const $dynamicTag = /* @__PURE__ */_._dynamic_tag("#text/0");
export function $setup($scope) {
$dynamicTag($scope, _hello);
}
export default /* @__PURE__ */_._template("__tests__/template.marko", $template, $walks, $setup);

View File

@ -1,6 +1,10 @@
Cannot mix Tags API and Class API features in the same file:
at packages/runtime-tags/src/__tests__/fixtures-interop/error-class-tags-dir/tags/hello.marko:1:1
> 1 | class {}
| ^^^^^ Unable to find entry point for [custom tag](https://markojs.com/docs/reference/custom-tag#relative-custom-tags) `<class>`.
2 |
3 | <h1>Hello world</h1>
Template file within a tags directory at packages/runtime-tags/src/__tests__/fixtures-interop/error-class-tags-dir/tags/hello.marko:
<class> tag at packages/runtime-tags/src/__tests__/fixtures-interop/error-class-tags-dir/tags/hello.marko(1,1):
> 1 | class {}
| ^^^^^
2 |
3 | <h1>Hello world</h1>

View File

@ -1,15 +1,6 @@
import { t as _t } from "marko/src/runtime/html/index.js";
const _marko_componentType = "__tests__/template.marko",
_marko_template = _t(_marko_componentType);
export default _marko_template;
import _hello from "./tags/hello.marko";
import _marko_tag from "marko/src/runtime/helpers/render-tag.js";
import _marko_renderer from "marko/src/runtime/components/renderer.js";
const _marko_component = {};
_marko_template._ = _marko_renderer(function (input, out, _componentDef, _component, state, $global) {
_marko_tag(_hello, {}, out, _componentDef, "0");
}, {
t: _marko_componentType,
i: true,
d: true
}, _marko_component);
import * as _ from "@marko/runtime-tags/debug/html";
export default _._template("__tests__/template.marko", input => {
const $scope0_id = _._scope_id();
_._dynamic_tag($scope0_id, "#text/0", _hello, {}, 0, 0, 0);
});

View File

@ -1,15 +1,17 @@
# Render
```html
<!---->
<!---->
<h1>
Hello world
</h1>
<!---->
<!---->
```
# Mutations
```
INSERT #text0, #text1, #text2, #comment0, #text3, #text4, #comment1, #text5, #text6, #text7
INSERT #comment0, #comment1, #text0, #text1, #comment2, #comment3
INSERT h1
INSERT h1/#text
```

View File

@ -1,4 +1,4 @@
import init6 from "virtual:./template.marko.hydrate-6.js import \"./tags/hello.marko\";\nexport default () => {};";
import init6 from "virtual:./template.marko.hydrate-6.js import \"./template.marko\";\nimport \"./tags/hello.marko\";\nexport default () => {};";
import init5 from "virtual:./template.marko.hydrate-5.js import { init } from \"marko/src/runtime/components/index.js\";\nimport \"./tags/components/hello-internal.marko\";\nexport default () => init();";
init6();
init5();

View File

@ -1,20 +1,8 @@
import "marko/src/runtime/helpers/tags-compat/dom-debug.mjs";
import { t as _t } from "marko/src/runtime/vdom/index.js";
const _marko_componentType = "__tests__/template.marko",
_marko_template = _t(_marko_componentType);
export default _marko_template;
import _hello from "./tags/hello.marko";
import _marko_dynamic_tag from "marko/src/runtime/helpers/dynamic-tag.js";
import _marko_renderer from "marko/src/runtime/components/renderer.js";
import { r as _marko_registerComponent } from "marko/src/runtime/components/registry.js";
_marko_registerComponent(_marko_componentType, () => _marko_template);
const _marko_component = {};
_marko_template._ = _marko_renderer(function (input, out, _componentDef, _component, state, $global) {
_marko_dynamic_tag(out, _hello, null, null, null, null, _componentDef, "0");
}, {
t: _marko_componentType,
i: true,
d: true
}, _marko_component);
import _marko_defineComponent from "marko/src/runtime/components/defineComponent.js";
_marko_template.Component = _marko_defineComponent(_marko_component, _marko_template._);
export const $template = `<!>${_hello_template}<!>`;
export const $walks = /* over(1), <hello>, over(1) */`b/${_hello_walks}&b`;
import { $setup as _hello, $template as _hello_template, $walks as _hello_walks } from "./tags/hello.marko";
export function $setup($scope) {
_hello($scope["#childScope/0"]);
}
import * as _ from "@marko/runtime-tags/debug/dom";
export default /* @__PURE__ */_._template("__tests__/template.marko", $template, $walks, $setup);

View File

@ -1,16 +1,6 @@
import "marko/src/runtime/helpers/tags-compat/html-debug.mjs";
import { t as _t } from "marko/src/runtime/html/index.js";
const _marko_componentType = "__tests__/template.marko",
_marko_template = _t(_marko_componentType);
export default _marko_template;
import _hello from "./tags/hello.marko";
import _marko_dynamic_tag from "marko/src/runtime/helpers/dynamic-tag.js";
import _marko_renderer from "marko/src/runtime/components/renderer.js";
const _marko_component = {};
_marko_template._ = _marko_renderer(function (input, out, _componentDef, _component, state, $global) {
_marko_dynamic_tag(out, _hello, null, null, null, null, _componentDef, "0");
}, {
t: _marko_componentType,
i: true,
d: true
}, _marko_component);
import * as _ from "@marko/runtime-tags/debug/html";
export default _._template("__tests__/template.marko", input => {
const $scope0_id = _._scope_id();
_hello({});
});

View File

@ -7,16 +7,17 @@
Hello world
</h1>
<script>
WALKER_RUNTIME("M")("s");
M.s.r = [_ =&gt; (_.a = [0, 1,
WALKER_RUNTIME("M")("_");
M._.r = [_ =&gt; (_.a = [0, 2,
{
m5c: "s1"
}]), "$compat_setScope 2"];
M.s.w();
m5c: "_0"
}])];
M._.w();
$MC = (window.$MC || []).concat(
{
"p": "_",
"w": [
["s1", 0,
["_0", 0,
{},
{
"f": 1
@ -25,7 +26,9 @@
"t": [
"__tests__/tags/components/hello-internal.marko"
]
})
});
M._.r.push("$compat_setScope 3");
M._.w()
</script>
</body>
</html>

View File

@ -1,27 +1,28 @@
# Write
<!--M#s1--><h1>Hello world</h1><!--M/--><script>WALKER_RUNTIME("M")("s");M.s.r=[_=>(_.a=[0,1,{m5c:"s1"}]),"$compat_setScope 2"];M.s.w();$MC=(window.$MC||[]).concat({"w":[["s1",0,{},{"f":1}]],"t":["__tests__/tags/components/hello-internal.marko"]})</script>
<!--M#_0--><h1>Hello world</h1><!--M/--><script>WALKER_RUNTIME("M")("_");M._.r=[_=>(_.a=[0,2,{m5c:"_0"}])];M._.w();$MC=(window.$MC||[]).concat({"p":"_","w":[["_0",0,{},{"f":1}]],"t":["__tests__/tags/components/hello-internal.marko"]});M._.r.push("$compat_setScope 3");M._.w()</script>
# Render End
```html
<html>
<head />
<body>
<!--M#s1-->
<!--M#_0-->
<h1>
Hello world
</h1>
<!--M/-->
<script>
WALKER_RUNTIME("M")("s");
M.s.r = [_ =&gt; (_.a = [0, 1,
WALKER_RUNTIME("M")("_");
M._.r = [_ =&gt; (_.a = [0, 2,
{
m5c: "s1"
}]), "$compat_setScope 2"];
M.s.w();
m5c: "_0"
}])];
M._.w();
$MC = (window.$MC || []).concat(
{
"p": "_",
"w": [
["s1", 0,
["_0", 0,
{},
{
"f": 1
@ -30,7 +31,9 @@
"t": [
"__tests__/tags/components/hello-internal.marko"
]
})
});
M._.r.push("$compat_setScope 3");
M._.w()
</script>
</body>
</html>

View File

@ -0,0 +1,5 @@
{
"name": "marko-test",
"version": "1.0.0",
"type": "commonjs"
}

View File

@ -22,50 +22,49 @@ type FeatureState = {
feature?: Feature;
};
const DEFAULT_FEATURE_TYPE = FeatureType.Class;
export function isTagsAPI(file = getFile()) {
const program = file.path;
let featureType = program.node.extra?.featureType;
const programExtra = (program.node.extra ??= {});
let { featureType } = programExtra;
if (!featureType) {
const lookup = getTaglibLookup(file);
const tagsDir = getTagsDir(file.opts.filename);
const programExtra = (program.node.extra ??= {});
if (tagsDir) {
featureType = programExtra.featureType = getTaglibLookup(
file,
).manualTagsDirs.has(tagsDir)
? FeatureType.Class
: FeatureType.Tags;
} else {
const state = {} as FeatureState;
scanBody(state, program.get("body"));
featureType = programExtra.featureType =
state.feature?.type || DEFAULT_FEATURE_TYPE;
const state = {} as FeatureState;
if (tagsDir && !lookup.manualTagsDirs?.has(tagsDir)) {
addFeature(
state,
FeatureType.Tags,
"Template file within a tags directory",
program,
);
}
scanBody(state, program.get("body"));
featureType = programExtra.featureType =
state.feature?.type ||
(lookup.exclusiveTagDiscoveryDirs === "tags"
? FeatureType.Tags
: FeatureType.Class);
}
return featureType === FeatureType.Tags;
}
const PATH_SEPARATOR_REGEX = /\/|\\/;
const TAGS_LENGTH = "tags".length;
const COMPONENTS_LENGTH = "components".length;
function getTagsDir(filename: string) {
const pathSeparator = PATH_SEPARATOR_REGEX.exec(filename)?.[0];
const pathSeparator = /\/|\\/.exec(filename)?.[0];
if (pathSeparator) {
let previousIndex = filename.length - 1;
while (previousIndex > 0) {
const index = filename.lastIndexOf(pathSeparator, previousIndex);
switch (previousIndex - index) {
case TAGS_LENGTH: {
case 4 /** "tags".length */: {
if (filename.startsWith("tags", index + 1)) {
return filename.slice(0, index + 5);
}
break;
}
case COMPONENTS_LENGTH: {
case 10 /** "components".length */: {
if (filename.startsWith("components", index + 1)) {
return false;
}
@ -97,6 +96,10 @@ function scanBody(
case "MarkoComment":
if (/^\s*use tags\s*$/.test((child.node as t.MarkoComment).value)) {
addFeature(state, FeatureType.Tags, "<!-- use tags -->", child);
} else if (
/^\s*use class\s*$/.test((child.node as t.MarkoComment).value)
) {
addFeature(state, FeatureType.Class, "<!-- use class -->", child);
}
break;
case "MarkoScriptlet":
@ -148,10 +151,21 @@ function scanTag(state: FeatureState, tag: t.NodePath<t.MarkoTag>) {
}
const tagDef = getTagDef(tag);
if (tagDef?.taglibId === runtimeInfo.taglibId) {
const feature = getFeatureTypeFromCoreTagName(tagDef.name);
if (feature) {
addFeature(state, feature, `<${tagDef.name}> tag`, tag.get("name"));
if (tagDef) {
if (tagDef.name === "style") {
if (
/^style(?:(?:\.[^.\s\\/:*?"<>|({]+)+)?\s*\{/.test(
tag.node.rawValue || "",
)
) {
addFeature(state, FeatureType.Class, `style block`, tag.get("name"));
}
} else if (tagDef.taglibId === runtimeInfo.taglibId) {
const feature = getFeatureTypeFromCoreTagName(tagDef.name);
if (feature) {
addFeature(state, feature, `<${tagDef.name}> tag`, tag.get("name"));
}
}
}
@ -197,7 +211,7 @@ function addFeature(
if (state.feature.type !== type) {
throw buildAggregateError(
path.hub.file,
'Cannot mix "tags api" and "class api" features',
"Cannot mix Tags API and Class API features in the same file",
[state.feature.name, state.feature.path],
[name, path],
);