Skip to content

Commit

Permalink
Xinit fix for where the application name has special characters (#5)
Browse files Browse the repository at this point in the history
* fix for x-init where the app name has special characters

* fix for x-init where the app name has special characters
  • Loading branch information
tsukhu authored Jul 28, 2020
1 parent 9a89758 commit 25c77cb
Show file tree
Hide file tree
Showing 2 changed files with 58 additions and 7 deletions.
23 changes: 17 additions & 6 deletions src/single-spa-alpinejs.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,16 @@ function bootstrap(opts, props) {
return Promise.resolve();
}

/**
* Remove special characters from the string
* @param {*} str
*/
function normalizeString(str) {
return str && typeof str === "string"
? str.replace(/[^a-z0-9,. ]/gi, "_")
: "";
}

/**
* Create the Alpine Element
* @param {*} template - The final template
Expand Down Expand Up @@ -81,7 +91,7 @@ function createAlpineElement(template, opts, props) {
childElement.id = `alpine-${props.name}`;
childElement.innerHTML = template;
const appName = props.appName || props.name;

const normalizedName = normalizeString(appName);
// add x-data attribute
childElement.setAttribute("x-data", JSON.stringify(finalData));

Expand All @@ -90,15 +100,15 @@ function createAlpineElement(template, opts, props) {
// create any global x-init functions that are needed
if (window.hasOwnProperty("singleSpaAlpineXInit")) {
// add new x-init function globally for the specific ID
window.singleSpaAlpineXInit[appName] = opts.xInit;
window.singleSpaAlpineXInit[normalizedName] = opts.xInit;
} else {
window.singleSpaAlpineXInit = { [appName]: opts.xInit };
window.singleSpaAlpineXInit = { [normalizedName]: opts.xInit };
}

// Add x-init attribute
childElement.setAttribute(
"x-init",
`singleSpaAlpineXInit.${appName}('alpine-${appName}')`
`singleSpaAlpineXInit.${normalizedName}('alpine-${appName}')`
);
}

Expand Down Expand Up @@ -157,6 +167,7 @@ function unmount(opts, props) {
return Promise.resolve().then(() => {
const domElementGetter = chooseDomElementGetter(opts, props);
const appName = props.appName || props.name;
const normalizedName = normalizeString(appName);
if (typeof domElementGetter !== "function") {
throw new Error(
`single-spa-alpinejs: the domElementGetter for application '${
Expand All @@ -180,13 +191,13 @@ function unmount(opts, props) {
if (opts.xInit) {
if (
!window.hasOwnProperty("singleSpaAlpineXInit") ||
typeof window.singleSpaAlpineXInit[`${appName}`] === "undefined"
typeof window.singleSpaAlpineXInit[`${normalizedName}`] === "undefined"
) {
throw new Error(
`single-spa-alpinejs: global function for xInit not found. Optional parameter opts.xInit if provided must be as a function that returns a promise`
);
}
delete window.singleSpaAlpineXInit[`${appName}`];
delete window.singleSpaAlpineXInit[`${normalizedName}`];
}
});
}
Expand Down
42 changes: 41 additions & 1 deletion src/single-spa-alpinejs.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,12 @@ describe(`single-spa-alpinejs`, () => {
return Object.assign({}, props, originalData);
};

function normalizeString(str) {
return str && typeof str === "string"
? str.replace(/[^a-z0-9,. ]/gi, "_")
: "";
}

const appOneTemplate = `
<div class="mui-panel" x-data="{ open: false }">
<div class="mui--test-display1">Test x-show</div>
Expand Down Expand Up @@ -227,7 +233,7 @@ describe(`single-spa-alpinejs`, () => {
);
expect(domEl.innerHTML.trim()).toBe(appTwoTemplate.trim());
})
.then(() => lifecycles.unmount(opts, props));
.then(() => lifecycles.unmount(props));
});

it(`renders function template with x-data as fn , x-init template and props`, () => {
Expand Down Expand Up @@ -290,6 +296,40 @@ describe(`single-spa-alpinejs`, () => {
.then(() => lifecycles.unmount(props));
});

it(`app name with special character renders function template with x-data as promise, x-init template and props`, () => {
const opts = {
template: appThreeTemplate,
xData: (data) => appThreeDataFn(data), // pass props to x-data
xInit: appThreeFn,
domElementGetter,
};
delete window["singleSpaAlpineXInit"];
const lifecycles = singleSpaAlpinejs(opts);
const appProps = { name: "@my/app" };
const appName = appProps.appName || appProps.name;
const normalizedAppName = normalizeString(appName);
return lifecycles
.bootstrap(appProps)
.then(() => lifecycles.mount(appProps))
.then(() => {
const domEl = domElementAlpineGetter(appProps.name);
expect(domEl.getAttribute("x-data").trim()).toBe(
JSON.stringify(
getCombinedProps(appProps, appThreeData(appProps))
).trim()
);
expect(domEl.getAttribute("x-init").trim()).toBe(
`singleSpaAlpineXInit.${normalizedAppName}('alpine-${appName}')`.trim()
);
expect(domEl.innerHTML.trim()).toBe(appThreeTemplate.trim());
expect(window.singleSpaAlpineXInit).toHaveProperty(
`${normalizedAppName}`,
appThreeFn
);
})
.then(() => lifecycles.unmount(appProps));
});

it(`renders function template with function with only template returning a promise`, () => {
const opts = {
template: () => Promise.resolve(appOneTemplate.trim()),
Expand Down

0 comments on commit 25c77cb

Please sign in to comment.