diff --git a/import-bulk.html b/import-bulk.html index 11b0fc29..afca8c97 100644 --- a/import-bulk.html +++ b/import-bulk.html @@ -102,6 +102,11 @@

Bulk Import

+
+ + Save as JSON + +
Save HTML for Document Authoring diff --git a/import.html b/import.html index d081b6ee..0168536b 100644 --- a/import.html +++ b/import.html @@ -116,6 +116,11 @@

Workbench

+
+ + Save as JSON + +
Save HTML for Document Authoring diff --git a/importer-guidelines.md b/importer-guidelines.md index a33748d4..9dbcf7c3 100644 --- a/importer-guidelines.md +++ b/importer-guidelines.md @@ -671,7 +671,24 @@ Every new project has its own collection of new use cases which might be totally - Linked images are not supported by Online Word thus they will be converted to image + link in Word. - Reuse the DOM elements from the orignal page, no need to re-create complete DOM structures, especially if the Markdown is what you need. Example: Text in a `div` will become a paragraph, no need to create a `p` tag and replace the `div`. More generally, the DOM can be dirty, as long as the output Markdown looks as expected, it does not matter. - If you import multiple page "types" for the project, you cannot either handle them in the same `import.js` file or have one `import-.js` file per type (or any filename convention you lie). Use the UI options to point to a different import filename. - + +## JSON import + +To import JSON, you need an `import.js` file with the following `transform` method: + +``` +export default { + transform: ({ + document, url, html, params, + }) => { + return { + path: `/.json`, + json: JSON.stringify(, null, 2) + } + }, +}; +``` + ## Debugging If you experience some deep nested Javascript exception, you can run the importer ui in developer mode, JS files will not be minified and obfuscated. Just run in the `/tools/importer/helix-importer-ui` folder: diff --git a/index.html b/index.html index 6be6c774..3677e3a7 100644 --- a/index.html +++ b/index.html @@ -102,6 +102,7 @@

Authoring Experience Selection

Document Authoring AEM Authoring + JSON Ok @@ -123,6 +124,7 @@

Authoring Experience Selection

Document Authoring AEM Authoring + JSON Reset diff --git a/js/import/import.preview.js b/js/import/import.preview.js index f20b81b6..4151ab78 100644 --- a/js/import/import.preview.js +++ b/js/import/import.preview.js @@ -19,6 +19,8 @@ const PreviewElements = Object.freeze({ TRANSFORMED_HTML_TEXTAREA: document.getElementById('import-transformed-html'), MD_SOURCE_TEXTAREA: document.getElementById('import-markdown-source'), MD_PREVIEW_PANEL: document.getElementById('import-markdown-preview'), + // use md preview panel for json + JSON_PREVIEW_PANEL: document.getElementById('import-markdown-preview'), IMPORT_FILE_PICKER_CONTAINER: document.getElementById('import-file-picker-container'), JCR_PANEL: document.getElementById('import-jcr'), }); @@ -153,7 +155,9 @@ const setupPreview = (parentSelector) => { return preview; }; -const loadPreview = ({ md, html: outputHTML, jcr }) => { +const loadPreview = ({ + md, html: outputHTML, jcr, json, +}) => { if (outputHTML) { preview.transformedEditor.setValue(html_beautify(outputHTML.replaceAll(/\s+/g, ' '), { indent_size: '2', @@ -181,7 +185,11 @@ const loadPreview = ({ md, html: outputHTML, jcr }) => { }); } else { preview.markdownEditor.setValue('No preview available.'); - preview.markdownPreview.innerHTML = 'No preview available.'; + if (json) { + preview.markdownPreview.innerHTML = `
${json}
`; + } else { + preview.markdownPreview.innerHTML = 'No preview available.'; + } } }; diff --git a/js/import/import.ui.js b/js/import/import.ui.js index 7b1301e4..0c5b056d 100644 --- a/js/import/import.ui.js +++ b/js/import/import.ui.js @@ -82,14 +82,14 @@ const enableProcessButtons = () => { const postSuccessfulStep = async (results, originalURL) => { let error = false; await asyncForEach(results, async ({ - docx, html, md, jcr, filename, path, report, from, + docx, html, md, jcr, json, filename, path, report, from, }) => { const data = { url: originalURL, path, }; - if (isSaveLocal && dirHandle && (docx || html || md || jcr)) { + if (isSaveLocal && dirHandle && (docx || html || md || jcr || json)) { const files = []; // if we were told to ave the doc file, add it to the list if (config.fields['import-local-docx'] && docx) { @@ -123,6 +123,10 @@ const postSuccessfulStep = async (results, originalURL) => { } } + if (config.fields['import-local-json'] && json) { + files.push({ type: 'json', filename: path, data: json }); + } + // if we were told to save the JCR package, add it to the list if (config.fields['import-jcr-package'] && jcr) { jcrPages.push({ @@ -385,7 +389,7 @@ const startImport = async () => { disableProcessButtons(); toggleLoadingButton(IMPORT_BUTTON); - isSaveLocal = config.fields['import-local-docx'] || config.fields['import-local-html'] || config.fields['import-local-md'] || config.fields['import-jcr-package'] || config.fields['import-local-da']; + isSaveLocal = config.fields['import-local-docx'] || config.fields['import-local-html'] || config.fields['import-local-md'] || config.fields['import-jcr-package'] || config.fields['import-local-da'] || config.fields['import-local-json']; if (isSaveLocal && !dirHandle) { try { dirHandle = await getDirectoryHandle(); diff --git a/js/shared/json-project.js b/js/shared/json-project.js new file mode 100644 index 00000000..6dae8631 --- /dev/null +++ b/js/shared/json-project.js @@ -0,0 +1,33 @@ +/* + * Copyright 2025 Adobe. All rights reserved. + * This file is licensed to you under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. You may obtain a copy + * of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS + * OF ANY KIND, either express or implied. See the License for the specific language + * governing permissions and limitations under the License. + */ + +/** + * @typedef {Object} JsonProjectConfig + * @property {string} origin - The base URL to fetch data from. + */ + +/** + * Represents a JSON project. + */ +const JsonProject = () => { + /** + * Retrieves the type of the project. + * @returns "json" The type of the project ('json'). + */ + const getType = () => 'json'; + + return { + getType, + }; +}; + +export default JsonProject; diff --git a/js/shared/pollimporter.js b/js/shared/pollimporter.js index c30d8e22..c2563a26 100644 --- a/js/shared/pollimporter.js +++ b/js/shared/pollimporter.js @@ -256,6 +256,14 @@ export default class PollImporter { this.running = false; return; } + } else if (projectType === 'json') { + const out = await WebImporter.html2json( + url, + documentClone, + this.projectTransform, + params, + ); + results = Array.isArray(out) ? out : [out]; } else { const out = await WebImporter.html2md( url, diff --git a/js/shared/project.js b/js/shared/project.js index 17ac113e..2cc09617 100644 --- a/js/shared/project.js +++ b/js/shared/project.js @@ -13,9 +13,10 @@ import DocProject from './doc-project.js'; import XWalkProject from './xwalk-project.js'; +import JsonProject from './json-project.js'; import { LOCAL_STORAGE_KEYS } from './localstorage.js'; /** - * @typedef {"doc" | "xwalk"} ProjectType + * @typedef {"doc" | "xwalk" | "json" } ProjectType * @description Represents the possible types of a project. */ @@ -34,6 +35,7 @@ const Project = async (config) => { const projectTypeMap = { doc: DocProject, xwalk: XWalkProject, + json: JsonProject, }; /** @@ -202,7 +204,10 @@ const Project = async (config) => { const updateUI = () => { const SAVE_AS_DOCX = document.getElementById('import-local-docx'); const DA_FIELD = document.getElementById('import-local-da'); + const LOCAL_HTML = document.getElementById('import-local-html'); + const LOCAL_MD = document.getElementById('import-local-md'); const XWALK_FIELDS = document.getElementById('xwalk'); + const JSON_FIELDS = document.getElementById('json'); const JCR_ASSET_FOLDER = document.getElementById('jcr-asset-folder'); const JCR_SITE_FOLDER = document.getElementById('jcr-site-folder'); @@ -234,6 +239,7 @@ const Project = async (config) => { if (projectType === 'doc') { if (XWALK_FIELDS) XWALK_FIELDS.remove(); + if (JSON_FIELDS) JSON_FIELDS.remove(); config.fields['import-jcr-package'] = false; @@ -242,6 +248,19 @@ const Project = async (config) => { if (jcrTab) { jcrTab.remove(); } + } else if (projectType === 'json') { + if (XWALK_FIELDS) XWALK_FIELDS.remove(); + if (SAVE_AS_DOCX) SAVE_AS_DOCX.remove(); + if (DA_FIELD) DA_FIELD.remove(); + if (LOCAL_HTML) LOCAL_HTML.remove(); + if (LOCAL_MD) LOCAL_MD.remove(); + + config.fields['import-local-docx'] = false; + config.fields['import-local-json'] = true; + config.fields['import-jcr-package'] = false; + config.fields['import-local-da'] = false; + config.fields['import-local-html'] = false; + config.fields['import-local-md'] = false; } else { if (SAVE_AS_DOCX) SAVE_AS_DOCX.remove(); if (DA_FIELD) DA_FIELD.remove(); diff --git a/modules/importer.js b/modules/importer.js index f1b2e3dd..71107427 100644 --- a/modules/importer.js +++ b/modules/importer.js @@ -25,6 +25,7 @@ import { html2md, md2jcr, rules, + html2json, } from '@adobe/helix-importer'; import docxStylesXML from '../resources/styles.xml'; @@ -83,6 +84,9 @@ const options = { async function html2mdWrapper(url, document, transformCfg, params) { return html2md(url, document, transformCfg, options, params); } +async function html2jsonWrapper(url, document, transformCfg, params) { + return html2json(url, document, transformCfg, options, params); +} async function html2docxWrapper(url, document, transformCfg, params) { return html2docx(url, document, transformCfg, options, params); @@ -106,6 +110,7 @@ export { FileUtils, JCRUtils, html2mdWrapper as html2md, + html2jsonWrapper as html2json, html2docxWrapper as html2docx, md2jcrWrapper as md2jcr, rules, diff --git a/package.json b/package.json index 84e3bd78..b47bd978 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,7 @@ }, "dependencies": { "@adobe/helix-html-pipeline": "6.26.6", - "@adobe/helix-importer": "3.4.101", + "@adobe/helix-importer": "https://github.com/adobe/helix-importer/tree/json", "@adobe/helix-importer-jcr-packaging": "2.0.10", "@spectrum-web-components/bundle": "1.7.0", "@spectrum-web-components/icons-workflow": "1.7.0",