@forsakringskassan/docs-generator 2.9.1 → 2.10.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.
@@ -3,7 +3,7 @@
3
3
  var path$1 = require('node:path');
4
4
  var esbuild = require('esbuild');
5
5
  var vue = require('vue');
6
- var vue3 = require('./vue3-BNuTiNOg.js');
6
+ var vue3 = require('./vue3-WhFYGWAf.js');
7
7
  var path = require('node:path/posix');
8
8
  require('@vue/compiler-sfc');
9
9
  require('./vendor-DM7XM4qm.js');
@@ -105,6 +105,7 @@ async function compileExample(batch) {
105
105
  external,
106
106
  tsconfig,
107
107
  define: {
108
+ "process.env.NODE_ENV": JSON.stringify("development"),
108
109
  "process.env.DOCS_ICON_LIB": JSON.stringify(iconLib)
109
110
  },
110
111
  plugins: [
package/dist/index.d.ts CHANGED
@@ -371,6 +371,7 @@ export declare interface NavigationSection {
371
371
  readonly path: string;
372
372
  readonly sortorder: number;
373
373
  readonly children: NavigationNode[];
374
+ readonly visible: boolean;
374
375
  }
375
376
 
376
377
  /**
@@ -619,6 +620,8 @@ export declare function themeSelectProcessor(): Processor;
619
620
  export declare interface TopnavEntry {
620
621
  path: string;
621
622
  title: string;
623
+ sortorder?: number;
624
+ visible?: boolean;
622
625
  }
623
626
 
624
627
  /**
package/dist/index.js CHANGED
@@ -13,10 +13,11 @@ var vueDocgenApi = require('vue-docgen-api');
13
13
  var fs$1 = require('node:fs');
14
14
  var sass = require('sass');
15
15
  var node_url = require('node:url');
16
+ var Module = require('node:module');
16
17
  var esbuild$1 = require('esbuild');
17
18
  var nunjucks = require('nunjucks');
18
19
  var vue = require('vue');
19
- var vue3 = require('./vue3-BNuTiNOg.js');
20
+ var vue3 = require('./vue3-WhFYGWAf.js');
20
21
  var express = require('express');
21
22
  require('fs');
22
23
  require('node:events');
@@ -440,6 +441,7 @@ function generateNavigation(entries) {
440
441
  key,
441
442
  sortorder: index,
442
443
  children: [],
444
+ visible: true,
443
445
  ...it
444
446
  };
445
447
  });
@@ -447,7 +449,8 @@ function generateNavigation(entries) {
447
449
  key: ".",
448
450
  path: "./index.html",
449
451
  sortorder: Infinity,
450
- children
452
+ children,
453
+ visible: true
451
454
  };
452
455
  }
453
456
  function topnavProcessor(filename, title) {
@@ -971,50 +974,63 @@ async function navigationFileReader(filePath, basePath) {
971
974
  return [doc];
972
975
  }
973
976
 
977
+ function resolveFrom(fromDirectory, moduleId) {
978
+ const fromFile = path.join(fs$1.realpathSync(fromDirectory), "noop.js");
979
+ return Module._resolveFilename(moduleId, {
980
+ id: fromFile,
981
+ filename: fromFile,
982
+ paths: Module._nodeModulePaths(fromDirectory)
983
+ });
984
+ }
974
985
  const WEBPACK_NODE_MODULE_PREFIX = "~";
975
- const moduleImporter = {
976
- findFileUrl(url) {
977
- let findUrl = url;
978
- if (url.startsWith(WEBPACK_NODE_MODULE_PREFIX)) {
979
- findUrl = url.substring(1);
980
- }
981
- const directory = path.dirname(findUrl);
982
- const fileName = path.basename(findUrl);
983
- const search = [
984
- `${fileName}.css`,
985
- `${fileName}.scss`,
986
- `_${fileName}.scss`,
987
- `${fileName}`
988
- ];
989
- for (const variant of search) {
990
- try {
991
- const moduleName = path.posix.join(directory, variant);
992
- const resolved = require.resolve(moduleName);
993
- return new URL(node_url.pathToFileURL(resolved));
994
- } catch (err) {
995
- if (err.code !== "MODULE_NOT_FOUND") {
996
- throw err;
986
+ function moduleImporter(options) {
987
+ const { cwd } = options;
988
+ return {
989
+ findFileUrl(url) {
990
+ let findUrl = url;
991
+ if (url.startsWith(WEBPACK_NODE_MODULE_PREFIX)) {
992
+ findUrl = url.substring(1);
993
+ }
994
+ const directory = path.dirname(findUrl);
995
+ const fileName = path.basename(findUrl);
996
+ const search = [
997
+ `${fileName}.css`,
998
+ `${fileName}.scss`,
999
+ `_${fileName}.scss`,
1000
+ `${fileName}`
1001
+ ];
1002
+ for (const variant of search) {
1003
+ try {
1004
+ const moduleName = path.posix.join(directory, variant);
1005
+ const resolved = resolveFrom(cwd, moduleName);
1006
+ return new URL(node_url.pathToFileURL(resolved));
1007
+ } catch (err) {
1008
+ if (err.code !== "MODULE_NOT_FOUND") {
1009
+ throw err;
1010
+ }
997
1011
  }
998
1012
  }
1013
+ return null;
999
1014
  }
1000
- return null;
1001
- }
1002
- };
1015
+ };
1016
+ }
1003
1017
 
1004
- async function compileSassString(dst, style) {
1018
+ async function compileSassString(dst, style, options = {}) {
1019
+ const { cwd = process.cwd() } = options;
1005
1020
  const result = await sass.compileStringAsync(style, {
1006
1021
  style: "expanded",
1007
- importers: [new sass.NodePackageImporter(), moduleImporter]
1022
+ importers: [new sass.NodePackageImporter(), moduleImporter({ cwd })]
1008
1023
  });
1009
1024
  await fs.mkdir(path.dirname(dst), { recursive: true });
1010
1025
  await fs.writeFile(dst, result.css, "utf-8");
1011
1026
  }
1012
1027
 
1013
- async function compileStyle(assetFolder, name, src) {
1028
+ async function compileStyle(assetFolder, name, src, options) {
1029
+ const { cwd } = options;
1014
1030
  try {
1015
1031
  const outfile = path$1.join("temp", `asset-${name}.css`);
1016
1032
  const source = await fs.readFile(src, "utf-8");
1017
- await compileSassString(outfile, source);
1033
+ await compileSassString(outfile, source, { cwd });
1018
1034
  const content = await fs.readFile(outfile, "utf-8");
1019
1035
  const fingerprint = createMarkdownRenderer.getFingerprint(content);
1020
1036
  const integrity = getIntegrity(content);
@@ -1042,7 +1058,8 @@ function byPriority$1({ options: a }, { options: b }) {
1042
1058
  function toSorted$1(values, comparator) {
1043
1059
  return [...values].sort(comparator);
1044
1060
  }
1045
- function cssAssetProcessor(assetFolder, assets) {
1061
+ function cssAssetProcessor(assetFolder, assets, options) {
1062
+ const { cwd } = options;
1046
1063
  return {
1047
1064
  name: "css-asset-processor",
1048
1065
  after: "assets",
@@ -1054,7 +1071,8 @@ function cssAssetProcessor(assetFolder, assets) {
1054
1071
  const info = await compileStyle(
1055
1072
  assetFolder,
1056
1073
  asset.name,
1057
- asset.src
1074
+ asset.src,
1075
+ { cwd }
1058
1076
  );
1059
1077
  const attrs = serializeAttrs(asset.options.attributes);
1060
1078
  const inject = { ...info, attrs: Array.from(attrs).join(" ") };
@@ -1347,18 +1365,20 @@ function getParentKey(name) {
1347
1365
  }
1348
1366
  function generateNavtree(docs) {
1349
1367
  const section = {};
1350
- function createSection(key, title, sortorder) {
1368
+ function createSection(key, title, sortorder, { visible } = {}) {
1351
1369
  const existing = section[key];
1352
1370
  if (existing) {
1353
1371
  existing.title = title;
1354
1372
  existing.sortorder = sortorder;
1373
+ existing.visible = visible ?? true;
1355
1374
  } else {
1356
1375
  const node = {
1357
1376
  key,
1358
1377
  title,
1359
1378
  path: "",
1360
1379
  sortorder,
1361
- children: []
1380
+ children: [],
1381
+ visible: visible ?? true
1362
1382
  };
1363
1383
  section[key] = node;
1364
1384
  if (key !== ".") {
@@ -1378,7 +1398,8 @@ function generateNavtree(docs) {
1378
1398
  title: parentKey.split("/").at(-1) ?? "(missing title)",
1379
1399
  path: "",
1380
1400
  sortorder: Infinity,
1381
- children: []
1401
+ children: [],
1402
+ visible: true
1382
1403
  };
1383
1404
  section[parentKey] = parent;
1384
1405
  let anchestor = parentKey;
@@ -1394,7 +1415,8 @@ function generateNavtree(docs) {
1394
1415
  title: "",
1395
1416
  path: "",
1396
1417
  sortorder: Infinity,
1397
- children: [current]
1418
+ children: [current],
1419
+ visible: true
1398
1420
  };
1399
1421
  section[anchestor] = node;
1400
1422
  current = node;
@@ -1425,7 +1447,7 @@ function generateNavtree(docs) {
1425
1447
  }
1426
1448
  if (!doc.visible) {
1427
1449
  if (isSection) {
1428
- createSection(name, title, sortorder);
1450
+ createSection(name, title, sortorder, { visible: false });
1429
1451
  }
1430
1452
  continue;
1431
1453
  }
@@ -1448,13 +1470,15 @@ function generateNavtree(docs) {
1448
1470
  existing.path = leafNode.path;
1449
1471
  existing.sortorder = sortorder;
1450
1472
  existing.children.unshift(leafNode);
1473
+ existing.visible = true;
1451
1474
  } else {
1452
1475
  const sectionNode = {
1453
1476
  key: name,
1454
1477
  title,
1455
1478
  path: leafNode.path,
1456
1479
  sortorder,
1457
- children: [leafNode]
1480
+ children: [leafNode],
1481
+ visible: true
1458
1482
  };
1459
1483
  section[name] = sectionNode;
1460
1484
  parent.children.push(sectionNode);
@@ -1479,7 +1503,8 @@ function generateNavtree(docs) {
1479
1503
  title: "",
1480
1504
  path: "",
1481
1505
  sortorder: Infinity,
1482
- children: []
1506
+ children: [],
1507
+ visible: true
1483
1508
  };
1484
1509
  }
1485
1510
  }
@@ -1639,7 +1664,7 @@ const cache$1 = /* @__PURE__ */ new Map();
1639
1664
  function cacheKey(layout, extension) {
1640
1665
  return [layout, extension].join("|");
1641
1666
  }
1642
- function findTemplate(from, src, format) {
1667
+ function findTemplate(folders, from, src, format) {
1643
1668
  let layout;
1644
1669
  if (typeof src === "string") {
1645
1670
  layout = src;
@@ -1653,12 +1678,14 @@ function findTemplate(from, src, format) {
1653
1678
  if (cached) {
1654
1679
  return cached;
1655
1680
  }
1681
+ folders = [...folders, templateDirectory];
1656
1682
  const template = `${layout}.template.${format}`;
1657
- const templateFile = path.join(templateDirectory, template);
1658
- if (!fs$1.existsSync(templateFile)) {
1659
- throw new MissingTemplateError(from, template, format, [
1660
- templateDirectory
1661
- ]);
1683
+ const searchPaths = folders.map((it) => {
1684
+ return path.join(it, template);
1685
+ });
1686
+ const found = searchPaths.find((it) => fs$1.existsSync(it));
1687
+ if (!found) {
1688
+ throw new MissingTemplateError(from, template, format, folders);
1662
1689
  }
1663
1690
  cache$1.set(key, template);
1664
1691
  return template;
@@ -1733,7 +1760,7 @@ async function compileExamples(options) {
1733
1760
  }
1734
1761
  }
1735
1762
  async function compileStandalones(options) {
1736
- const { fileInfo, tasks, renderTemplate, templateData } = options;
1763
+ const { fileInfo, tasks, renderTemplate, templateData, templateFolders } = options;
1737
1764
  if (tasks.length === 0) {
1738
1765
  return;
1739
1766
  }
@@ -1741,7 +1768,11 @@ async function compileStandalones(options) {
1741
1768
  const outputFolder = path.posix.join(options.outputFolder, fileInfo.path);
1742
1769
  const cacheMiss = cache(cacheFolder, outputFolder);
1743
1770
  const dirtyTasks = tasks.filter(cacheMiss);
1744
- const standaloneTemplate = findTemplate(fileInfo, "example");
1771
+ const standaloneTemplate = findTemplate(
1772
+ templateFolders,
1773
+ fileInfo,
1774
+ "example"
1775
+ );
1745
1776
  for (const task of dirtyTasks) {
1746
1777
  const outputFile = path.join(outputFolder, task.outputFile);
1747
1778
  const content = await renderTemplate(standaloneTemplate, {
@@ -1757,7 +1788,7 @@ function createTemplateLoader(folders) {
1757
1788
  }
1758
1789
  async function render(doc, docs, nav, vendors, options) {
1759
1790
  const { fileInfo } = doc;
1760
- const { outputFolder, cacheFolder } = options;
1791
+ const { outputFolder, cacheFolder, templateFolders } = options;
1761
1792
  if (!haveOutputFile(fileInfo)) {
1762
1793
  return null;
1763
1794
  }
@@ -1771,7 +1802,7 @@ async function render(doc, docs, nav, vendors, options) {
1771
1802
  const njk = new nunjucks.Environment(loader, { autoescape: false });
1772
1803
  const asyncRender = util.promisify(njk.render);
1773
1804
  const renderTemplate = asyncRender.bind(njk);
1774
- const template = findTemplate(doc.fileInfo, doc);
1805
+ const template = findTemplate(templateFolders, doc.fileInfo, doc);
1775
1806
  const templateData = {
1776
1807
  ...options.templateData,
1777
1808
  site: options.site,
@@ -1892,6 +1923,7 @@ async function render(doc, docs, nav, vendors, options) {
1892
1923
  fileInfo,
1893
1924
  cacheFolder,
1894
1925
  outputFolder,
1926
+ templateFolders,
1895
1927
  tasks: generatedStandalone,
1896
1928
  renderTemplate,
1897
1929
  templateData
@@ -2015,9 +2047,10 @@ async function compileVendor(assetFolder, vendor, options) {
2015
2047
  platform: "browser",
2016
2048
  tsconfig,
2017
2049
  define: {
2018
- "process.env.NODE_ENV": JSON.stringify(
2019
- process.env.NODE_ENV ?? "production"
2020
- )
2050
+ "process.env.NODE_ENV": JSON.stringify("development"),
2051
+ __VUE_OPTIONS_API__: "true",
2052
+ __VUE_PROD_DEVTOOLS__: "true",
2053
+ __VUE_PROD_HYDRATION_MISMATCH_DETAILS__: "false"
2021
2054
  },
2022
2055
  alias: vendor.alias ? { [`${vendor.package}`]: vendor.alias } : void 0,
2023
2056
  ...options
@@ -2231,7 +2264,6 @@ async function stage(stage2, context, processors, { verbose } = { verbose: true
2231
2264
  }
2232
2265
  } catch (err) {
2233
2266
  console.error(`When running processor "${processor.name}":`);
2234
- console.error(err);
2235
2267
  throw err;
2236
2268
  }
2237
2269
  }
@@ -2247,14 +2279,16 @@ function createContext(templateLoader) {
2247
2279
  title: "",
2248
2280
  path: "",
2249
2281
  sortorder: Infinity,
2250
- children: []
2282
+ children: [],
2283
+ visible: true
2251
2284
  };
2252
2285
  let sidenav = {
2253
2286
  key: ".",
2254
2287
  title: "",
2255
2288
  path: "",
2256
2289
  sortorder: Infinity,
2257
- children: []
2290
+ children: [],
2291
+ visible: true
2258
2292
  };
2259
2293
  const templateData = {};
2260
2294
  return {
@@ -2413,6 +2447,7 @@ class Generator {
2413
2447
  return { pages };
2414
2448
  }
2415
2449
  async build(sourceFiles) {
2450
+ const cwd = process.cwd();
2416
2451
  this.sourceFiles = sourceFiles;
2417
2452
  const {
2418
2453
  site,
@@ -2427,7 +2462,7 @@ class Generator {
2427
2462
  const processors = [
2428
2463
  fileReaderProcessor(sourceFiles),
2429
2464
  vendorProcessor(assetFolder, this.vendor),
2430
- cssAssetProcessor(assetFolder, this.styles),
2465
+ cssAssetProcessor(assetFolder, this.styles, { cwd }),
2431
2466
  jsAssetProcessor(assetFolder, this.scripts),
2432
2467
  staticResourcesProcessor(assetFolder, this.resources),
2433
2468
  navigationProcessor(),
@@ -179,7 +179,7 @@ pre code {
179
179
  .docs-topnav__item {
180
180
  display: inline-flex;
181
181
  list-style-type: none;
182
- padding: 1.1rem 0 0 0;
182
+ padding: 1.1rem 0 0;
183
183
  white-space: nowrap;
184
184
  }
185
185
  .docs-topnav__item.highlight {
@@ -832,10 +832,7 @@ h1 code {
832
832
  /* Page header - inverted */
833
833
  header .page-header {
834
834
  background-color: #fff;
835
- padding-top: 0.5rem;
836
- padding-bottom: 0.5rem;
837
- padding-left: 1.2rem;
838
- padding-right: 1.2rem;
835
+ padding: 0.5rem 1.2rem;
839
836
  border-bottom: 1px solid #ddddde;
840
837
  flex-wrap: wrap;
841
838
  }
@@ -179,7 +179,7 @@ pre code {
179
179
  .docs-topnav__item {
180
180
  display: inline-flex;
181
181
  list-style-type: none;
182
- padding: 1.1rem 0 0 0;
182
+ padding: 1.1rem 0 0;
183
183
  white-space: nowrap;
184
184
  }
185
185
  .docs-topnav__item.highlight {
@@ -832,10 +832,7 @@ h1 code {
832
832
  /* Page header - inverted */
833
833
  header .page-header {
834
834
  background-color: #fff;
835
- padding-top: 0.5rem;
836
- padding-bottom: 0.5rem;
837
- padding-left: 1.2rem;
838
- padding-right: 1.2rem;
835
+ padding: 0.5rem 1.2rem;
839
836
  border-bottom: 1px solid #ddddde;
840
837
  flex-wrap: wrap;
841
838
  }
@@ -5,7 +5,7 @@
5
5
  "toolPackages": [
6
6
  {
7
7
  "packageName": "@microsoft/api-extractor",
8
- "packageVersion": "7.47.9"
8
+ "packageVersion": "7.47.11"
9
9
  }
10
10
  ]
11
11
  }
@@ -12754,51 +12754,82 @@ function generateCode(options) {
12754
12754
  const selector = `#${slug}`;
12755
12755
  const asset = `${slug}-${fingerprint}.js`;
12756
12756
  const { descriptor, errors } = require$$0.parse(code, { filename });
12757
+ const scopeId = `data-v-${fingerprint}`;
12757
12758
  if (errors.length) {
12758
12759
  throw new Error(`Errors occured when trying to parse ${filename}.`);
12759
12760
  }
12760
- if (descriptor.template?.content) {
12761
- descriptor.template.content = descriptor.template.content.replace(
12762
- "\\\\",
12763
- "\\"
12764
- );
12765
- }
12766
- if (descriptor.styles.some((it) => it.scoped)) {
12767
- throw new Error(
12768
- `Scoped style in ${filename}. Scoped styles are not implemented.`
12769
- );
12770
- }
12771
- const styleContent = descriptor.styles.map((stylePart) => stylePart.content).join("\n");
12761
+ const hasScoped = descriptor.styles.some((e) => e.scoped);
12762
+ const styleContent = descriptor.styles.map((stylePart) => {
12763
+ const compiledStyle = require$$0.compileStyle({
12764
+ filename: descriptor.filename,
12765
+ source: stylePart.content,
12766
+ isProd: false,
12767
+ id: scopeId,
12768
+ scoped: stylePart.scoped,
12769
+ trim: true
12770
+ });
12771
+ return compiledStyle.code;
12772
+ }).join("\n");
12772
12773
  let sourcecode = `import { setup } from "${setupPath}";
12773
12774
  `;
12774
- if (descriptor.script?.content) {
12775
- sourcecode += descriptor.script.content.replace(
12776
- "export default",
12777
- "const exampleComponent = "
12778
- );
12775
+ let bindings = void 0;
12776
+ if (descriptor.script || descriptor.scriptSetup) {
12777
+ const script = require$$0.compileScript(descriptor, {
12778
+ id: scopeId,
12779
+ isProd: false,
12780
+ sourceMap: false,
12781
+ inlineTemplate: false,
12782
+ genDefaultAs: "exampleComponent",
12783
+ templateOptions: {
12784
+ id: scopeId,
12785
+ filename,
12786
+ source: descriptor.template?.content,
12787
+ scoped: hasScoped,
12788
+ slotted: descriptor.slotted,
12789
+ compilerOptions: {
12790
+ comments: false,
12791
+ whitespace: "condense",
12792
+ scopeId: hasScoped ? scopeId : void 0,
12793
+ mode: "module"
12794
+ }
12795
+ }
12796
+ });
12797
+ bindings = script.bindings;
12798
+ sourcecode += script.content;
12799
+ sourcecode += "\n\n";
12779
12800
  } else {
12780
12801
  sourcecode += `const exampleComponent = {};
12802
+
12781
12803
  `;
12782
12804
  }
12783
12805
  if (descriptor.template) {
12784
- const js = require$$0.compileTemplate({
12785
- id: filename,
12806
+ const template = require$$0.compileTemplate({
12807
+ id: scopeId,
12786
12808
  filename,
12787
12809
  source: descriptor.template.content,
12810
+ scoped: hasScoped,
12811
+ slotted: descriptor.slotted,
12788
12812
  preprocessLang: descriptor.template.lang,
12789
12813
  compilerOptions: {
12814
+ bindingMetadata: bindings,
12790
12815
  comments: false,
12791
- whitespace: "preserve"
12816
+ whitespace: "condense",
12817
+ scopeId: hasScoped ? scopeId : void 0,
12818
+ mode: "module"
12792
12819
  }
12793
12820
  });
12794
- sourcecode += `${js.code}
12821
+ sourcecode += `${template.code}
12795
12822
 
12796
12823
  `;
12797
12824
  sourcecode += `exampleComponent.render = render;
12798
-
12799
12825
  `;
12800
12826
  }
12801
- sourcecode += `setup({
12827
+ if (hasScoped) {
12828
+ sourcecode += `exampleComponent.__scopeId = "${scopeId}";
12829
+ `;
12830
+ }
12831
+ sourcecode += `
12832
+ setup({
12802
12833
  rootComponent: exampleComponent,
12803
12834
  selector: "${selector}"
12804
12835
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@forsakringskassan/docs-generator",
3
- "version": "2.9.1",
3
+ "version": "2.10.0",
4
4
  "description": "Documentation generator",
5
5
  "keywords": [
6
6
  "documentation"
@@ -5,10 +5,11 @@
5
5
  {% set navPath = nav.path | replace("html", "") %}
6
6
  {% set navPath = navPath | replace(".", "") %}
7
7
  {% set activePage = navPath in doc.fileInfo.fullPath %}
8
+ {% if nav.visible %}
8
9
  <li class="docs-topnav__item {% if activePage %}highlight{% endif %}">
9
10
  <a class="docs-topnav__anchor" href="{{ nav.path | relative(doc) }}"> {{ nav.title }} </a>
10
11
  </li>
11
- {%- endfor -%}
12
+ {% endif %} {%- endfor -%}
12
13
  <li class="docs-topnav__item docs-topnav__item--more" style="visibility: hidden">
13
14
  <button type="button" class="docs-topnav__anchor" aria-expanded="false">
14
15
  <span>Mer</span>
@@ -23,10 +24,11 @@
23
24
  {% set navPath = nav.path | replace("html", "") %}
24
25
  {% set navPath = navPath | replace(".", "") %}
25
26
  {% set activePage = navPath in doc.fileInfo.fullPath %}
27
+ {% if nav.visible %}
26
28
  <li class="docs-contextmenu__item {% if activePage %}highlight{% endif %}" style="display: none">
27
29
  <a class="docs-contextmenu__anchor" href="{{ nav.path | relative(doc) }}"> {{ nav.title }} </a>
28
30
  </li>
29
- {%- endfor -%}
31
+ {% endif %} {%- endfor -%}
30
32
  </ul>
31
33
  </div>
32
34
  </li>