@salty-css/astro 0.1.0-alpha.9 → 0.1.0-refactor-add-additional-paths-to-config-cache.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/astro-vite-plugin.cjs +6 -6
- package/astro-vite-plugin.d.ts +12 -1
- package/astro-vite-plugin.js +108 -91
- package/integration.cjs +1 -1
- package/integration.d.ts +16 -0
- package/integration.js +25 -16
- package/package.json +2 -2
package/astro-vite-plugin.cjs
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
"use strict";Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});const
|
|
1
|
+
"use strict";Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});const S=require("@salty-css/core/compiler/helpers"),G=require("@salty-css/core/compiler/get-function-range"),j=require("@salty-css/core/server"),I=require("@salty-css/core/util"),f=require("fs/promises"),x=require("path"),M=D=>{const n=D.compiler;return{name:"stylegen",configureServer:function(t){n.importFile=async e=>{const r=Date.now();return t.ssrLoadModule(`${e}?t=${r}`)}},configResolved:async function(){try{await n.generateCss()}catch(t){throw console.error("Error during initial CSS generation:",t),t}},load:async function(t){var e,r;try{if(S.isSaltyFile(t)){const d=await n.getDestDir();if(/.+\?configFile=(\w+).+/.test(t)){const y=new URLSearchParams(t.split("?")[1]).get("configFile");if(!y)return;const m=x.join(d,"astro",y),w=await f.readFile(m,"utf-8");if(!w)return;try{const p=JSON.parse(w),{clientProps:F={},classNames:i="",tagIsComponent:N,tagName:a="div"}=p,$=["import { resolveAstroProps } from '@salty-css/astro/element-props';",...p.imports||[]],R=N?a:`__r.element || ${JSON.stringify(F.element||a)}`;return`---
|
|
2
2
|
${$.join(`
|
|
3
3
|
`)}
|
|
4
4
|
const __gp = ${JSON.stringify(F)};
|
|
5
|
-
const __r = resolveAstroProps(Astro.props, __gp, ${JSON.stringify(
|
|
6
|
-
const Element = ${
|
|
5
|
+
const __r = resolveAstroProps(Astro.props, __gp, ${JSON.stringify(i)});
|
|
6
|
+
const Element = ${R};
|
|
7
7
|
---
|
|
8
|
-
<Element class:list={__r.class} style={__r.style} {...__r.rest}><slot/></Element>`}catch(
|
|
8
|
+
<Element class:list={__r.class} style={__r.style} {...__r.rest}><slot/></Element>`}catch(p){console.error("Error parsing config file:",p);return}}const h=["import { classNameInstance } from '@salty-css/core/instances/classname-instance';"],l=[],s=[],u=await f.readFile(t,"utf-8"),O=await n.compileSaltyFile(t,d),E=Object.entries(O.contents);for(const[o,y]of E){const m=await S.resolveExportValue(y,1);if(!m.generator)continue;if(m.isClassName){const _=m.generator._withBuildContext({callerName:o,isProduction:n.isProduction,config:{}});l.push(`const ${o} = classNameInstance(${JSON.stringify(_.params)});`),s.push(o);continue}const[w,p]=await G.getFunctionRange(u,o),F=u.slice(w,p),i=(r=(e=/styled\(([^,]+),/.exec(F))==null?void 0:e.at(1))==null?void 0:r.trim();if(!i){console.warn(`Could not determine tag name for ${o} in ${t}`);continue}const N=m.generator._withBuildContext({callerName:o,isProduction:n.isProduction,config:{}}),a={componentName:o,tagName:i.replace(/['"`]/g,""),tagIsComponent:!1,classNames:N.classNames,imports:[],clientProps:N.clientProps},v=/^\w+$/.test(i);if(a.tagIsComponent=v,v){const _=E.some(([g])=>g===i),P=u.match(new RegExp(`import[^;]*${i}[^;]*;`));if(_){const g=I.toHash(i),J=`import ${i} from '${t}.astro?configFile=${g}.config';`;a.imports=[J]}else if(P){const g=P.at(0);a.imports=[g]}}const $=x.join(d,"astro");await f.readFile($,"utf-8").catch(()=>!1)||await f.mkdir($,{recursive:!0});const C=I.toHash(o),q=x.join(d,"astro",`${C}.config`);await f.writeFile(q,JSON.stringify(a)),h.push(`import ${o} from '${t}.astro?configFile=${C}.config';`),s.push(o)}return`${h.join(`
|
|
9
9
|
`)}
|
|
10
|
-
${
|
|
10
|
+
${l.join(`
|
|
11
11
|
`)}
|
|
12
|
-
export { ${
|
|
12
|
+
export { ${s.join(", ")} };`}return}catch(c){console.error("Error during file compilation:",c);return}},handleHotUpdate:async function({file:t,server:e,modules:r}){try{if(await j.checkShouldRestart(t))return await n.generateCss(!1);if(!S.isSaltyFile(t))return;const h=await n.getDestDir(),l=[];for(const[s,u]of e.moduleGraph.idToModuleMap)s.startsWith(t+".astro?configFile=")&&(e.moduleGraph.invalidateModule(u),l.push(u));for(const s of e.moduleGraph.urlToModuleMap.values())s.file&&s.file.startsWith(h)&&(e.moduleGraph.invalidateModule(s),l.push(s));return e.ws.send({type:"update",updates:[]}),[...r,...l]}catch(c){console.error("Error during hot update handling:",c)}},watchChange:{handler:async function(t,e){try{S.isSaltyFile(t)&&e.event!=="delete"&&(await j.checkShouldRestart(t)||await n.generateFile(t))}catch(r){console.error("Error during watch change handling:",r)}}}}};exports.default=M;exports.saltyPlugin=M;
|
package/astro-vite-plugin.d.ts
CHANGED
|
@@ -1,3 +1,14 @@
|
|
|
1
|
+
import { SaltyCompiler, SaltyCompilerMode } from '@salty-css/core/compiler/salty-compiler';
|
|
1
2
|
import { PluginOption } from 'vite';
|
|
2
|
-
export
|
|
3
|
+
export interface SaltyAstroPluginOptions {
|
|
4
|
+
/**
|
|
5
|
+
* Explicit build mode. Defaults to NODE_ENV-based detection.
|
|
6
|
+
*/
|
|
7
|
+
mode?: SaltyCompilerMode;
|
|
8
|
+
/**
|
|
9
|
+
* Reuse an existing SaltyCompiler instance instead of creating a new one.
|
|
10
|
+
*/
|
|
11
|
+
compiler: SaltyCompiler;
|
|
12
|
+
}
|
|
13
|
+
export declare const saltyPlugin: (options: SaltyAstroPluginOptions) => PluginOption;
|
|
3
14
|
export default saltyPlugin;
|
package/astro-vite-plugin.js
CHANGED
|
@@ -1,117 +1,134 @@
|
|
|
1
|
-
import { isSaltyFile as
|
|
2
|
-
import { getFunctionRange as
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import { readFile as v, mkdir as B, writeFile as G } from "fs/promises";
|
|
1
|
+
import { isSaltyFile as v, resolveExportValue as G } from "@salty-css/core/compiler/helpers";
|
|
2
|
+
import { getFunctionRange as A } from "@salty-css/core/compiler/get-function-range";
|
|
3
|
+
import { checkShouldRestart as D } from "@salty-css/core/server";
|
|
4
|
+
import { toHash as P } from "@salty-css/core/util";
|
|
5
|
+
import { readFile as S, mkdir as H, writeFile as k } from "fs/promises";
|
|
7
6
|
import { join as x } from "path";
|
|
8
|
-
const
|
|
9
|
-
const
|
|
7
|
+
const q = (M) => {
|
|
8
|
+
const n = M.compiler;
|
|
10
9
|
return {
|
|
11
10
|
name: "stylegen",
|
|
12
|
-
configureServer(t) {
|
|
13
|
-
|
|
14
|
-
const
|
|
15
|
-
return t.ssrLoadModule(`${
|
|
11
|
+
configureServer: function(t) {
|
|
12
|
+
n.importFile = async (o) => {
|
|
13
|
+
const r = Date.now();
|
|
14
|
+
return t.ssrLoadModule(`${o}?t=${r}`);
|
|
16
15
|
};
|
|
17
16
|
},
|
|
18
|
-
|
|
19
|
-
|
|
17
|
+
configResolved: async function() {
|
|
18
|
+
try {
|
|
19
|
+
await n.generateCss();
|
|
20
|
+
} catch (t) {
|
|
21
|
+
throw console.error("Error during initial CSS generation:", t), t;
|
|
22
|
+
}
|
|
20
23
|
},
|
|
21
24
|
load: async function(t) {
|
|
22
|
-
var
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
${$.join(`
|
|
25
|
+
var o, r;
|
|
26
|
+
try {
|
|
27
|
+
if (v(t)) {
|
|
28
|
+
const f = await n.getDestDir();
|
|
29
|
+
if (/.+\?configFile=(\w+).+/.test(t)) {
|
|
30
|
+
const h = new URLSearchParams(t.split("?")[1]).get("configFile");
|
|
31
|
+
if (!h) return;
|
|
32
|
+
const p = x(f, "astro", h), y = await S(p, "utf-8");
|
|
33
|
+
if (!y) return;
|
|
34
|
+
try {
|
|
35
|
+
const u = JSON.parse(y), { clientProps: w = {}, classNames: i = "", tagIsComponent: $, tagName: a = "div" } = u, F = ["import { resolveAstroProps } from '@salty-css/astro/element-props';", ...u.imports || []], I = $ ? a : `__r.element || ${JSON.stringify(w.element || a)}`;
|
|
36
|
+
return `---
|
|
37
|
+
${F.join(`
|
|
36
38
|
`)}
|
|
37
|
-
const __gp = ${JSON.stringify(
|
|
38
|
-
const __r = resolveAstroProps(Astro.props, __gp, ${JSON.stringify(
|
|
39
|
-
const Element = ${
|
|
39
|
+
const __gp = ${JSON.stringify(w)};
|
|
40
|
+
const __r = resolveAstroProps(Astro.props, __gp, ${JSON.stringify(i)});
|
|
41
|
+
const Element = ${I};
|
|
40
42
|
---
|
|
41
43
|
<Element class:list={__r.class} style={__r.style} {...__r.rest}><slot/></Element>`;
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
44
|
+
} catch (u) {
|
|
45
|
+
console.error("Error parsing config file:", u);
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
45
48
|
}
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
49
|
+
const g = ["import { classNameInstance } from '@salty-css/core/instances/classname-instance';"], l = [], s = [], m = await S(t, "utf-8"), O = await n.compileSaltyFile(t, f), E = Object.entries(O.contents);
|
|
50
|
+
for (const [e, h] of E) {
|
|
51
|
+
const p = await G(h, 1);
|
|
52
|
+
if (!p.generator) continue;
|
|
53
|
+
if (p.isClassName) {
|
|
54
|
+
const _ = p.generator._withBuildContext({
|
|
55
|
+
callerName: e,
|
|
56
|
+
isProduction: n.isProduction,
|
|
57
|
+
config: {}
|
|
58
|
+
});
|
|
59
|
+
l.push(`const ${e} = classNameInstance(${JSON.stringify(_.params)});`), s.push(e);
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
62
|
+
const [y, u] = await A(m, e), w = m.slice(y, u), i = (r = (o = /styled\(([^,]+),/.exec(w)) == null ? void 0 : o.at(1)) == null ? void 0 : r.trim();
|
|
63
|
+
if (!i) {
|
|
64
|
+
console.warn(`Could not determine tag name for ${e} in ${t}`);
|
|
65
|
+
continue;
|
|
66
|
+
}
|
|
67
|
+
const $ = p.generator._withBuildContext({
|
|
68
|
+
callerName: e,
|
|
69
|
+
isProduction: n.isProduction,
|
|
55
70
|
config: {}
|
|
56
|
-
})
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
classNames: w.classNames,
|
|
74
|
-
imports: [],
|
|
75
|
-
clientProps: w.clientProps
|
|
76
|
-
}, N = /^\w+$/.test(s);
|
|
77
|
-
if (a.tagIsComponent = N, N) {
|
|
78
|
-
const _ = I.some(([f]) => f === s), E = F.match(new RegExp(`import[^;]*${s}[^;]*;`));
|
|
79
|
-
if (_) {
|
|
80
|
-
const f = j(s), M = `import ${s} from '${t}.astro?configFile=${f}.config';`;
|
|
81
|
-
a.imports = [M];
|
|
82
|
-
} else if (E) {
|
|
83
|
-
const f = E.at(0);
|
|
84
|
-
a.imports = [f];
|
|
71
|
+
}), a = {
|
|
72
|
+
componentName: e,
|
|
73
|
+
tagName: i.replace(/['"`]/g, ""),
|
|
74
|
+
tagIsComponent: !1,
|
|
75
|
+
classNames: $.classNames,
|
|
76
|
+
imports: [],
|
|
77
|
+
clientProps: $.clientProps
|
|
78
|
+
}, N = /^\w+$/.test(i);
|
|
79
|
+
if (a.tagIsComponent = N, N) {
|
|
80
|
+
const _ = E.some(([d]) => d === i), R = m.match(new RegExp(`import[^;]*${i}[^;]*;`));
|
|
81
|
+
if (_) {
|
|
82
|
+
const d = P(i), J = `import ${i} from '${t}.astro?configFile=${d}.config';`;
|
|
83
|
+
a.imports = [J];
|
|
84
|
+
} else if (R) {
|
|
85
|
+
const d = R.at(0);
|
|
86
|
+
a.imports = [d];
|
|
87
|
+
}
|
|
85
88
|
}
|
|
89
|
+
const F = x(f, "astro");
|
|
90
|
+
await S(F, "utf-8").catch(() => !1) || await H(F, { recursive: !0 });
|
|
91
|
+
const C = P(e), j = x(f, "astro", `${C}.config`);
|
|
92
|
+
await k(j, JSON.stringify(a)), g.push(`import ${e} from '${t}.astro?configFile=${C}.config';`), s.push(e);
|
|
86
93
|
}
|
|
87
|
-
|
|
88
|
-
await v($, "utf-8").catch(() => !1) || await B($, { recursive: !0 });
|
|
89
|
-
const C = j(o), D = x(m, "astro", `${C}.config`);
|
|
90
|
-
await G(D, JSON.stringify(a)), p.push(`import ${o} from '${t}.astro?configFile=${C}.config';`), i.push(o);
|
|
91
|
-
}
|
|
92
|
-
return `${p.join(`
|
|
94
|
+
return `${g.join(`
|
|
93
95
|
`)}
|
|
94
|
-
${
|
|
96
|
+
${l.join(`
|
|
95
97
|
`)}
|
|
96
|
-
export { ${
|
|
98
|
+
export { ${s.join(", ")} };`;
|
|
99
|
+
}
|
|
100
|
+
return;
|
|
101
|
+
} catch (c) {
|
|
102
|
+
console.error("Error during file compilation:", c);
|
|
103
|
+
return;
|
|
97
104
|
}
|
|
98
105
|
},
|
|
99
|
-
handleHotUpdate: async ({ file: t, server:
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
+
handleHotUpdate: async function({ file: t, server: o, modules: r }) {
|
|
107
|
+
try {
|
|
108
|
+
if (await D(t)) return await n.generateCss(!1);
|
|
109
|
+
if (!v(t)) return;
|
|
110
|
+
const g = await n.getDestDir(), l = [];
|
|
111
|
+
for (const [s, m] of o.moduleGraph.idToModuleMap)
|
|
112
|
+
s.startsWith(t + ".astro?configFile=") && (o.moduleGraph.invalidateModule(m), l.push(m));
|
|
113
|
+
for (const s of o.moduleGraph.urlToModuleMap.values())
|
|
114
|
+
s.file && s.file.startsWith(g) && (o.moduleGraph.invalidateModule(s), l.push(s));
|
|
115
|
+
return o.ws.send({ type: "update", updates: [] }), [...r, ...l];
|
|
116
|
+
} catch (c) {
|
|
117
|
+
console.error("Error during hot update handling:", c);
|
|
118
|
+
}
|
|
106
119
|
},
|
|
107
120
|
watchChange: {
|
|
108
|
-
handler: async (t,
|
|
109
|
-
|
|
121
|
+
handler: async function(t, o) {
|
|
122
|
+
try {
|
|
123
|
+
v(t) && o.event !== "delete" && (await D(t) || await n.generateFile(t));
|
|
124
|
+
} catch (r) {
|
|
125
|
+
console.error("Error during watch change handling:", r);
|
|
126
|
+
}
|
|
110
127
|
}
|
|
111
128
|
}
|
|
112
129
|
};
|
|
113
130
|
};
|
|
114
131
|
export {
|
|
115
|
-
|
|
116
|
-
|
|
132
|
+
q as default,
|
|
133
|
+
q as saltyPlugin
|
|
117
134
|
};
|
package/integration.cjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});const
|
|
1
|
+
"use strict";Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});const u=require("@salty-css/core/compiler/salty-compiler"),s=require("@salty-css/core/compiler/copy-config-cache"),g=require("./astro-vite-plugin.cjs"),d=require("path"),f=require("url"),a=(n={})=>{let e,t;return{name:"astro-salty-integration",hooks:{"astro:config:setup":({config:o,updateConfig:r})=>{const{srcDir:i="src",rootDir:c=o.root.pathname,mode:l}=n;t=d.join(c,i),e=new u.SaltyCompiler(t,{mode:l}),r({vite:{plugins:[g.default({compiler:e})]}})},"astro:build:done":async({dir:o})=>{if(!e||!t||!e.isProduction)return;const r=f.fileURLToPath(o),i=s.resolveCopyConfigCacheDestinations(n.copyConfigCache,r,t);await s.copyConfigCacheTo(e,i)}}}};exports.default=a;exports.saltyIntegration=a;
|
package/integration.d.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { AstroIntegration } from 'astro';
|
|
2
|
+
import { SaltyCompilerMode } from '@salty-css/core/compiler/salty-compiler';
|
|
3
|
+
import { CopyConfigCacheOption } from '@salty-css/core/compiler/copy-config-cache';
|
|
2
4
|
interface SaltyIntegrationOptions {
|
|
3
5
|
/**
|
|
4
6
|
* The source directory where SaltyCSS Config is located.
|
|
@@ -10,6 +12,20 @@ interface SaltyIntegrationOptions {
|
|
|
10
12
|
* Default is the current directory of the Astro configuration.
|
|
11
13
|
*/
|
|
12
14
|
rootDir?: string;
|
|
15
|
+
/**
|
|
16
|
+
* Explicit build mode. Defaults to NODE_ENV-based detection.
|
|
17
|
+
*/
|
|
18
|
+
mode?: SaltyCompilerMode;
|
|
19
|
+
/**
|
|
20
|
+
* Where to copy the Salty `config-cache.json` after a production build.
|
|
21
|
+
*
|
|
22
|
+
* - `true` (default) → copy to Astro's `dist` output dir.
|
|
23
|
+
* - `false` → no copy.
|
|
24
|
+
* - `string | string[]` → copy to the default destination PLUS each listed path.
|
|
25
|
+
* Useful for adapters that emit a separate server bundle (e.g. Cloudflare's
|
|
26
|
+
* `dist/_worker.js/`, Vercel's `.vercel/output/functions/...`).
|
|
27
|
+
*/
|
|
28
|
+
copyConfigCache?: CopyConfigCacheOption;
|
|
13
29
|
}
|
|
14
30
|
export declare const saltyIntegration: (options?: SaltyIntegrationOptions) => AstroIntegration;
|
|
15
31
|
export default saltyIntegration;
|
package/integration.js
CHANGED
|
@@ -1,19 +1,28 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
1
|
+
import { SaltyCompiler as c } from "@salty-css/core/compiler/salty-compiler";
|
|
2
|
+
import { resolveCopyConfigCacheDestinations as m, copyConfigCacheTo as l } from "@salty-css/core/compiler/copy-config-cache";
|
|
3
|
+
import f from "./astro-vite-plugin.js";
|
|
4
|
+
import { join as p } from "path";
|
|
5
|
+
import { fileURLToPath as u } from "url";
|
|
6
|
+
const D = (e = {}) => {
|
|
7
|
+
let o, t;
|
|
8
|
+
return {
|
|
9
|
+
name: "astro-salty-integration",
|
|
10
|
+
hooks: {
|
|
11
|
+
"astro:config:setup": ({ config: r, updateConfig: i }) => {
|
|
12
|
+
const { srcDir: n = "src", rootDir: a = r.root.pathname, mode: s } = e;
|
|
13
|
+
t = p(a, n), o = new c(t, { mode: s }), i({
|
|
14
|
+
vite: { plugins: [f({ compiler: o })] }
|
|
15
|
+
});
|
|
16
|
+
},
|
|
17
|
+
"astro:build:done": async ({ dir: r }) => {
|
|
18
|
+
if (!o || !t || !o.isProduction) return;
|
|
19
|
+
const i = u(r), n = m(e.copyConfigCache, i, t);
|
|
20
|
+
await l(o, n);
|
|
21
|
+
}
|
|
13
22
|
}
|
|
14
|
-
}
|
|
15
|
-
}
|
|
23
|
+
};
|
|
24
|
+
};
|
|
16
25
|
export {
|
|
17
|
-
|
|
18
|
-
|
|
26
|
+
D as default,
|
|
27
|
+
D as saltyIntegration
|
|
19
28
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@salty-css/astro",
|
|
3
|
-
"version": "0.1.0-
|
|
3
|
+
"version": "0.1.0-refactor-add-additional-paths-to-config-cache.0",
|
|
4
4
|
"main": "./dist/index.js",
|
|
5
5
|
"module": "./dist/index.mjs",
|
|
6
6
|
"typings": "./dist/index.d.ts",
|
|
@@ -78,7 +78,7 @@
|
|
|
78
78
|
}
|
|
79
79
|
},
|
|
80
80
|
"dependencies": {
|
|
81
|
-
"@salty-css/core": "^0.1.0-
|
|
81
|
+
"@salty-css/core": "^0.1.0-refactor-add-additional-paths-to-config-cache.0",
|
|
82
82
|
"astro": "^5.13.2",
|
|
83
83
|
"vite": "^6.3.5"
|
|
84
84
|
}
|