Skip to content

Instantly share code, notes, and snippets.

@NicKKKolass
Created October 16, 2025 10:52
Show Gist options
  • Select an option

  • Save NicKKKolass/90fcb081982e0e5416081490f7de2260 to your computer and use it in GitHub Desktop.

Select an option

Save NicKKKolass/90fcb081982e0e5416081490f7de2260 to your computer and use it in GitHub Desktop.
import { writeFile, readdir, readFile } from 'node:fs/promises';
import path from 'path';
/*
This code is not mine, and only did a few modifications which instead exports the notes into separate txt files instead of the original's result of exporting it into a single txt file.
*/
/*
Steps to get/export your notes from Huawei Notes:
1. Login into a Huawei Account in the phone.
2. Activate in your phone, the Notes Syncing, which is inside of Huawei Account > Cloud
3. Log in into https://cloud.huawei.com
4. Go to https://cloud.huawei.com/home#/account/gdpr, and click on Download Notes
5. This will give you a zip file with a password. Extract the zip file into a folder.
6. Copy this file into the folder as index.mjs
7. You'll need NodeJs installed for this: https://nodejs.org/en/
I made and tested this on v18+, but v19 and v16 should also work
8. open a console/terminal (see how to do that in your OS), and run in it "node index.mjs"
9. Your notes should be in the notes.txt file inside of the same folder.
Extra:
The script only copies the text content, as simple text,
and it doesn't copy the title or other stuff that your notes might contain.
I left a comment in the portion of code that might be helpful to modify if
you want more information, such as the creation time, or other info.
*/
// Helper function to sanitize file names
function sanitizeFileName(title) {
if (!title) return 'Untitled'; // Fallback if title is empty
return title
.replace(/[^a-z0-9]/gi, '_') // Replace non-alphanumeric characters with underscores
.replace(/__+/g, '_') // Replace multiple underscores with a single one
.replace(/^_|_$/g, '') // Remove leading/trailing underscores
.toLowerCase(); // Convert to lowercase
}
async function readNotes() {
console.log(`πŸ“ | Huawei Notes HTML -> TXT (Modified with title-based file names and error fallback)`);
const __dirname = path.resolve(path.dirname(''));
console.log(`πŸ“ | > Reading Directory`, __dirname);
const folders = [];
const files = await readdir(__dirname, {
withFileTypes: true,
});
files.forEach((file) => {
if (file.isDirectory()) folders.push(file.name);
});
console.log(`πŸ“ | > Notes found: `, folders.length);
const notesWithDetails = await Promise.all(
folders.map(async (folder) => {
const route = `${__dirname}/${folder}/json.js`;
try {
const weirdJs = await readFile(route, 'utf8');
const noteData = JSON.parse(weirdJs.replace('var data = ', '')).content;
const title = noteData.title ? noteData.title.trim() : 'Untitled';
const content = noteData.content ? noteData.content.split('|')[1].trim() : '';
return {
folder: folder,
title: title,
content: content
};
} catch (reason) {
console.error(`πŸ› | > Error: `, route, reason);
return null;
}
})
);
const cleanedUpNotes = notesWithDetails.filter(item => item && item.content);
console.log(`πŸ“ | > Total after removing empty or errored: `, cleanedUpNotes.length);
let fallbackCounter = 1; // Global counter for fallback names
for (const [index, note] of cleanedUpNotes.entries()) {
const sanitizedBaseName = sanitizeFileName(note.title); // Sanitized title
const proposedFileName = `${sanitizedBaseName}.txt`; // e.g., my_note.txt
try {
// First, check if the file already exists (as before)
let finalFileName = proposedFileName;
let counter = 1;
while (await fileExists(finalFileName)) {
finalFileName = `${sanitizedBaseName}_${counter}.txt`; // e.g., my_note_1.txt
counter++;
}
await writeFile(finalFileName, note.content, 'utf8');
console.log(`πŸ“ | Exported note "${note.title}" to ${finalFileName}`);
} catch (error) {
// If writing fails (e.g., ENOENT), use fallback name
if (error.code === 'ENOENT' || error.code === 'EINVAL') { // Handle ENOENT or invalid names
const fallbackFileName = `Untitled_${fallbackCounter}.txt`; // e.g., Untitled_1.txt
fallbackCounter++; // Increment for the next fallback
await writeFile(fallbackFileName, note.content, 'utf8'); // Try writing with fallback
console.log(`πŸ“ | Fallback: Exported note "${note.title}" to ${fallbackFileName} due to error: ${error.code}`);
} else {
console.error(`πŸ› | > Error exporting note "${note.title}": `, error);
}
}
}
console.log(`πŸ“ | All notes successfully exported as separate files! πŸŽ‰`);
}
// Helper function to check if a file exists
async function fileExists(filePath) {
try {
await readFile(filePath);
return true;
} catch {
return false;
}
}
readNotes();
/*
MIT License
Copyright Β© 2022 Santiago Persico
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the β€œSoftware”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED β€œAS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment