@mfeflow/core 0.1.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/features/tailwind-prefix.d.ts +8 -0
- package/dist/index.js +48 -7
- package/package.json +2 -2
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Tailwind class prefix of an app. Tailwind 4 accepts lowercase letters only, so a name made of
|
|
3
|
+
* letters is its own prefix (`cart:flex`); any other name keeps its letters and gets four more from a hash
|
|
4
|
+
* of the whole name, so `cart_b` and `cartb` (and `cart1` and `cart2`) never share one.
|
|
5
|
+
*/
|
|
6
|
+
export declare function tailwindPrefix(name: string): string;
|
|
7
|
+
/** Throws if two apps map to the same prefix, which would bring back the collision the prefix prevents. */
|
|
8
|
+
export declare function assertUniqueTailwindPrefixes(names: readonly string[], prefixOf?: (name: string) => string): void;
|
package/dist/index.js
CHANGED
|
@@ -1921,6 +1921,7 @@ function assertThirdPartyContent(manifest, label) {
|
|
|
1921
1921
|
if (manifest.name.startsWith('@mfeflow/') || !manifest.name.startsWith('@')) throw new MfeflowError('E203', `${label}: third-party features must be namespaced and cannot use @mfeflow`);
|
|
1922
1922
|
for (const file of manifest.files ?? []){
|
|
1923
1923
|
if (void 0 !== file.from) throw new MfeflowError('E203', `${label}: "${file.to}" reads a local file (from); use inline content`);
|
|
1924
|
+
if (file.atRoot) throw new MfeflowError('E203', `${label}: "${file.to}" uses atRoot, which is first-party only`);
|
|
1924
1925
|
assertThirdPartyPath(file.to, file.to);
|
|
1925
1926
|
}
|
|
1926
1927
|
for (const t of manifest.transforms ?? []){
|
|
@@ -1929,7 +1930,10 @@ function assertThirdPartyContent(manifest, label) {
|
|
|
1929
1930
|
];
|
|
1930
1931
|
for (const file of files)assertThirdPartyPath(file, file);
|
|
1931
1932
|
}
|
|
1932
|
-
for (const dep of manifest.dependencies ?? [])
|
|
1933
|
+
for (const dep of manifest.dependencies ?? []){
|
|
1934
|
+
if (dep.name.includes('{{')) throw new MfeflowError('E203', `${label}: ${dep.name} names a package in the project's scope`);
|
|
1935
|
+
if (!isRegistryRange(dep.range)) throw new MfeflowError('E203', `${label}: ${dep.name}@${dep.range} is not a registry version range`);
|
|
1936
|
+
}
|
|
1933
1937
|
for (const t of manifest.transforms ?? [])assertThirdPartyTransform(t, label);
|
|
1934
1938
|
for (const script of manifest.scripts ?? [])assertPlainScript(script.name, label);
|
|
1935
1939
|
}
|
|
@@ -2018,6 +2022,31 @@ async function listLocalFeatures(featuresDir, ops = realFs) {
|
|
|
2018
2022
|
const checked = await Promise.all(dirs.map(async (dir)=>await readIfExists(ops, node_path.join(featuresDir, dir, 'feature.json')) === void 0 ? void 0 : dir));
|
|
2019
2023
|
return checked.filter((d)=>void 0 !== d).toSorted((a, b)=>a.localeCompare(b));
|
|
2020
2024
|
}
|
|
2025
|
+
const LETTERS = 'abcdefghijklmnopqrstuvwxyz';
|
|
2026
|
+
function shortHash(text) {
|
|
2027
|
+
let hash = 0x811c9dc5;
|
|
2028
|
+
for (const char of text)hash = Math.imul(hash ^ char.codePointAt(0), 0x01000193) >>> 0;
|
|
2029
|
+
let out = '';
|
|
2030
|
+
for(let i = 0; i < 4; i++){
|
|
2031
|
+
out += LETTERS[hash % 26];
|
|
2032
|
+
hash = Math.floor(hash / 26);
|
|
2033
|
+
}
|
|
2034
|
+
return out;
|
|
2035
|
+
}
|
|
2036
|
+
function tailwindPrefix(name) {
|
|
2037
|
+
if (/^[a-z]+$/.test(name)) return name;
|
|
2038
|
+
const letters = name.replace(/[^a-z]/g, '') || 'app';
|
|
2039
|
+
return letters + shortHash(name);
|
|
2040
|
+
}
|
|
2041
|
+
function assertUniqueTailwindPrefixes(names, prefixOf = tailwindPrefix) {
|
|
2042
|
+
const seen = new Map();
|
|
2043
|
+
for (const name of names){
|
|
2044
|
+
const prefix = prefixOf(name);
|
|
2045
|
+
const other = seen.get(prefix);
|
|
2046
|
+
if (void 0 !== other) throw new MfeflowError('E208', `the apps "${other}" and "${name}" would both use the Tailwind prefix "${prefix}"; rename one of them`);
|
|
2047
|
+
seen.set(prefix, name);
|
|
2048
|
+
}
|
|
2049
|
+
}
|
|
2021
2050
|
const TURBO_FILE = 'turbo.json';
|
|
2022
2051
|
const EXACT_APP = 'app:';
|
|
2023
2052
|
function placesFor(target, project) {
|
|
@@ -2068,6 +2097,9 @@ function resolveOptions(manifest, given = {}) {
|
|
|
2068
2097
|
function applies(part, options, present) {
|
|
2069
2098
|
return Object.entries(part.when ?? {}).every(([name, value])=>name.startsWith(FEATURE_CONDITION) ? present.has(name.slice(FEATURE_CONDITION.length)) === value : options[name] === value);
|
|
2070
2099
|
}
|
|
2100
|
+
function fill(text, data, label) {
|
|
2101
|
+
return text.includes('{{') ? substitute(text, data, label) : text;
|
|
2102
|
+
}
|
|
2071
2103
|
function resolve_assertWritable(file, relative, trusted) {
|
|
2072
2104
|
if (!isSafeRelativePath(file)) throw new MfeflowError('E105', `"${file}"`);
|
|
2073
2105
|
if (trusted) assertWritable(file);
|
|
@@ -2095,6 +2127,7 @@ async function resolveFeature(input) {
|
|
|
2095
2127
|
for (const other of manifest.conflicts ?? [])if (present.has(other)) throw new MfeflowError('E205', `${manifest.name} and ${other}`);
|
|
2096
2128
|
for (const needed of manifest.requires ?? [])if (!present.has(needed)) throw new MfeflowError('E206', `${manifest.name} needs ${needed}`);
|
|
2097
2129
|
const options = resolveOptions(manifest, input.options);
|
|
2130
|
+
if ('tailwind' === manifest.name) assertUniqueTailwindPrefixes(Object.keys(project.apps));
|
|
2098
2131
|
const dataFor = (place)=>({
|
|
2099
2132
|
scope: project.scope,
|
|
2100
2133
|
topology: project.topology,
|
|
@@ -2108,18 +2141,26 @@ async function resolveFeature(input) {
|
|
|
2108
2141
|
name: place.name,
|
|
2109
2142
|
kind: place.kind,
|
|
2110
2143
|
path: place.dir,
|
|
2111
|
-
port: place.port
|
|
2144
|
+
port: place.port,
|
|
2145
|
+
tailwindPrefix: tailwindPrefix(place.name)
|
|
2112
2146
|
}
|
|
2113
2147
|
});
|
|
2114
2148
|
const steps = [];
|
|
2115
|
-
for (const file of (manifest.files ?? []).filter((f)=>applies(f, options, present)))
|
|
2116
|
-
|
|
2117
|
-
|
|
2118
|
-
|
|
2149
|
+
for (const file of (manifest.files ?? []).filter((f)=>applies(f, options, present))){
|
|
2150
|
+
if (file.atRoot && !trusted) throw new MfeflowError('E203', `${manifest.name}: "${file.to}" uses atRoot, which only features that ship with mfeflow may`);
|
|
2151
|
+
for (const place of placesFor(file.target, project)){
|
|
2152
|
+
const data = dataFor(place);
|
|
2153
|
+
const relative = fill(file.to, data, `${manifest.name}:${file.to}`);
|
|
2154
|
+
const to = file.atRoot ? relative : joinPosix(place.dir, relative);
|
|
2155
|
+
resolve_assertWritable(to, relative, trusted);
|
|
2156
|
+
steps.push(async ()=>builder.create(to, await contentOf(file, feature, data)));
|
|
2157
|
+
}
|
|
2119
2158
|
}
|
|
2120
2159
|
for (const dep of (manifest.dependencies ?? []).filter((d)=>applies(d, options, present)))for (const place of placesFor(dep.target, project)){
|
|
2121
2160
|
const pkg = joinPosix(place.dir, 'package.json');
|
|
2122
|
-
|
|
2161
|
+
if (!trusted && dep.name.includes('{{')) throw new MfeflowError('E203', `${manifest.name}: ${dep.name} is for first-party features`);
|
|
2162
|
+
const name = fill(dep.name, dataFor(place), `${manifest.name}:${dep.name}`);
|
|
2163
|
+
steps.push(()=>builder.addDependency(pkg, name, dep.range, dep.dev));
|
|
2123
2164
|
}
|
|
2124
2165
|
for (const script of (manifest.scripts ?? []).filter((x)=>applies(x, options, present)))for (const place of placesFor(script.target, project)){
|
|
2125
2166
|
const pkg = joinPosix(place.dir, 'package.json');
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mfeflow/core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "The engine behind the mfeflow CLI: project model, change plans with diffs, atomic apply with rollback, and feature resolution.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"micro-frontend",
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
"dependencies": {
|
|
46
46
|
"@babel/parser": "8.0.10",
|
|
47
47
|
"@babel/types": "8.0.10",
|
|
48
|
-
"@mfeflow/contracts": "0.
|
|
48
|
+
"@mfeflow/contracts": "0.3.0",
|
|
49
49
|
"diff": "9.0.0",
|
|
50
50
|
"eta": "4.6.0",
|
|
51
51
|
"semver": "7.8.5",
|