Skip to content

Commit a1d00db

Browse files
authored
Templating modular refactor (#661)
* Remove logging in some tests * Skip sanitizing tests until we know what eduard is doing with frontmatter escaping * Do not write out blank/empty template render * improve note getting * fix await bug. refactor promptKey to remove varName * add promptKey tests * Prompts refactor * Add note.openTasks and other similar funcs * promptTag and promptMention * Create AddingNewPromptCommands.md * fix empty tag on promptKey * await bugs fixed * URL changes * NTemplating basically working; needs better error handling * Improvement in template syntax checking - working well * templating linting wip * more wip JSON rewriting bug persists * Adding windsurf and claude to ignores * fix smart-quotes replacement * add getNote * fixed some of the errors * test cleanups * real world tests in progress * add ability to pass newNoteTitle or other args to templateNew * Add ability to pass the newNoteName to templateNew * Update change log with all the fixes * Fix BasePromptHandler * Fix JSON validation tests. break out to other file. * Flow clean-ups * Fix More Templating Tests * removing json validation entirely * Fix a bunch of tests * More Templating test fixes * Fixes to include tag * Fix Prompt Edge Cases * Lots of improvements/clean-ups * Error messaging improvement * various promptDate fixes * Prompt fixes WIP * Fix to single var prompts not showing the message * Various bug fixes * wip allow for ESC to stop execution * add journalingQuestion and tighten prompt tests * Adding Templating API endpoint checks * remove double await in bible verse * Remove the zillions of realworld tests which are not needed * Fixed some TemplateRunner edge cases * Making templatejs code more resilient * Add warning about ternaries etc opening a line * date module fixes * Lots of tests and clean-ups * Fix a test * fixes to now/timestamp and error tweaks * Adding tasks module * Fix for ignoring prompts in comment tags * wip fixes for prompt cancels * test passing after escape fix * fix some edges * Fix promptKey * promptKey hand-enter escape stops prompts now * Fix for tag frontmatter regex test * new test for default value parsing * Fix for parameter parsing bug * Add error handling for mismatched tags * clean up logs * Fix async bug * Added JSDOC and inline comments prior to refactor * base refactor * Break down render() * All tests including integration tests are passing * Tests all pass and code builds * Bug fixes in rendering - still userData bug * fix system time bug * Edge fix for when prompts are chosen in promptKey * Remove console.logs * Update datePicker warning message * remove incremental rendering * Add originalScript parameter to TemplatingEngine for better error reporting - Modified TemplatingEngine constructor to accept originalScript parameter - Updated error handling to include original script in error messages - Updated all TemplatingEngine instantiations to pass original template data - Updated all test files to maintain compatibility with new constructor signature * Using AI for error suggestions * Templating errors should work when offline also * Fix edge where fm had error but body was plain text * TemplateEngine refactor/split * adding weather logs * fix * Differentiate logs * log reduce * Improve newline slurping for variable settings * Lessened logging * Some log improvements * Fixed a lot of edges in prompts * Fix remaining tests & remove logs * Reduce test logging * Add top level NP objects to the AIAnalyzer prompt * fix multiline JS which was failing * Fix for bug with comment tags * Update templateProcessor.test.js * MeetingNotes almost working with DataStore.invoke * rename renderTemplate duplicate named function * Adding terser back to reduce rolled up size of scripts * Added eventDate() and eventEndDate() to render pipeline * removed redundancy in data/methods props * move getValuesForKey to frontmatter module * Add frontmatter module * change frontMatter to frontmatter (no cap) * Fix frontmatter module bug * rename frontMatter to frontmatter in other plugins * fix stringUtils bug * Add tests for stringUtils * Optimize looking for prompts * Fix 8601date bug that was localizing * Update CHANGELOG.md * daysUntil will now return negative dates * Update StandardPromptHandler.js * remove example promptList and cance lon promptDate ESC * clean up - remove extra modules directory * Fix circularity caused by getTags * add datePicker back for legacy * Fix invokeCommand/isCommandAvail bug * ensure moment-with-locales is the only one used * Adding momentWrapper to deal with NP weeks * Abandoning momentWrapper - did not work * Test for noteplan week formatting * Fix startOfWeek and endOfWeek etc to use NP week * Delete DateModule_Documentation_Updates.md * add <select foldername * add auto-updating Templating * Update CHANGELOG.md * Skip tests because of mock * Add <select * misc doc update * Fix multi-line JS in a tag bug * add stoicQuote * added getRandomLine * Some improvements to AI error analysis * Changelog update * Allow include with template strings * fix frontmatter edge cases - Fix bug where frontmatter was not being processed correctly if it started with "---" - Fix bug where templatejs code blocks were not being processed correctly and no other tags - Fix bug where template with frontmatter (--) was not being processed when it had no other templating tags * Update CHANGELOG.md * Additional logging * fix curly quotes * Fix renderTemplate() bug that was showing frontmatter in result * Fix user edge cases with <%_ * Add test for multi-line slurps * Add web.services to await funcs list * Add longer timeout bc advice was timing out * Fix import casing * Fix error in rollup for minify * Change error messaging for web services * Add hard-coded advice * Fix TemplateRunner Bug * Move blank note with folder in template fm * Remove some logs
1 parent 266de53 commit a1d00db

182 files changed

Lines changed: 37806 additions & 4527 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,3 +23,5 @@ docs/index.html
2323
Documentation-Helpers
2424
**/react.*.dev.js
2525
**/react.*.min.js
26+
.windsurfrules
27+
CLAUDE.md

TasksModule_docs.md

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
import Link from 'next/link'
2+
import Callout from '@/components/Callout'
3+
4+
# Tasks Module
5+
6+
## Overview
7+
8+
The Tasks Module provides methods for interacting with tasks within NotePlan.
9+
10+
<Callout
11+
type="info"
12+
description={`
13+
This module allows you to retrieve and manipulate tasks from your notes, ensuring they are synchronized with block IDs for reliable referencing.
14+
`}
15+
/>
16+
17+
## Methods
18+
19+
> namespace: `tasks`
20+
21+
The following are the methods available in the Tasks Module. They can be used in any `Templating` template; no additional configuration is required.
22+
23+
---
24+
25+
### getSyncedOpenTasksFrom
26+
27+
> #### async getSyncedOpenTasksFrom(sourceIdentifier : string) : Promise<string>
28+
>
29+
> Retrieves open tasks (including their sub-tasks/children) from a specified note (daily, weekly, monthly, quarterly, yearly calendar note, or a project note). It ensures each open task paragraph and its children have a block ID and returns a string with each task on a new line.
30+
31+
- `sourceIdentifier` - (string) Specifies the note to retrieve tasks from. This can be:
32+
- `'<today>'`: Fetches tasks from today's daily note.
33+
- `'<yesterday>'`: Fetches tasks from yesterday's daily note.
34+
- An ISO 8601 date string for a specific calendar note:
35+
- Daily: `"YYYYMMDD"` (e.g., `"20230410"`) or `"YYYY-MM-DD"` (e.g., `"2023-04-10"`)
36+
- Weekly: `"YYYY-Www"` (e.g., `"2023-W24"`)
37+
- Monthly: `"YYYY-MM"` (e.g., `"2023-10"`)
38+
- Quarterly: `"YYYY-Qq"` (e.g., `"2023-Q4"`)
39+
- Yearly: `"YYYY"` (e.g., `"2023"`)
40+
- The title of a project note (string).
41+
42+
- `-> result` - (Promise<string>) Returns a promise that resolves to a string containing all open tasks and their sub-tasks from the specified note, each on a new line. If the note is not found or contains no open tasks, it resolves to an empty string.
43+
44+
**Behavior Notes:**
45+
46+
* The method uses `getOpenTasksAndChildren` to identify open tasks and their hierarchical children.
47+
* It automatically adds block IDs to any open task or child task paragraph that doesn't already have one. This modification happens directly in the NotePlan data store.
48+
* If multiple project notes match a given title, the method will use the first one found and log a debug message.
49+
50+
**Examples**
51+
52+
The following example retrieves open tasks from today's daily note:
53+
54+
```javascript
55+
<%- await tasks.getSyncedOpenTasksFrom('<today>') %>
56+
```
57+
58+
The following example retrieves open tasks from a specific weekly note:
59+
60+
```javascript
61+
<%- await tasks.getSyncedOpenTasksFrom('2023-W42') %>
62+
```
63+
64+
The following example retrieves open tasks from a project note titled "My Project Q4":
65+
66+
```javascript
67+
<%- await tasks.getSyncedOpenTasksFrom('My Project Q4') %>
68+
```

__mocks__/Calendar.mock.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
* For functions: check whether async or not & add params & return value
77
*
88
*/
9-
import moment from 'moment'
9+
import moment from 'moment/min/moment-with-locales'
1010
import * as chrono from 'chrono-node'
1111

1212
export const Calendar = {

__mocks__/Editor.mock.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ export const Editor = new Proxy(editorOverrides, {
4444
if (prop in target) {
4545
return target[prop]
4646
}
47-
if (prop in target.note) {
47+
if (prop && target.note && prop in target.note) {
4848
return target.note[prop]
4949
}
5050
// Handle known built-in Symbol properties with sensible defaults

__mocks__/NotePlan.mock.js

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,20 @@ export class NotePlan {
2929
// async resetCaches() { return null },
3030
selectedSidebarFolder = `SelectedFolder`
3131
// async showConfigurationView() { return null },
32-
constructor(data?: any = {}) {
32+
33+
/**
34+
* Mock AI function for testing
35+
*/
36+
static ai(prompt, filenames = [], useStrictFilenames = false, model = 'gpt-4') {
37+
// Return a mock AI response for testing
38+
return Promise.resolve(`Mock AI Analysis: This appears to be a template error. Please check your variable definitions and syntax.`)
39+
}
40+
41+
constructor(data = {}) {
3342
this.__update(data)
3443
}
3544

36-
__update(data?: any = {}) {
45+
__update(data = {}) {
3746
Object.keys(data).forEach((key) => {
3847
this[key] = data[key]
3948
})

dbludeau.TodoistNoteplanSync/src/NPPluginMain.js

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
* logError(pluginJson,"All users will see these fatal/error messages")
3030
*/
3131

32-
import { getFrontMatterAttributes } from '../../helpers/NPFrontMatter'
32+
import { getFrontmatterAttributes } from '../../helpers/NPFrontMatter'
3333
import { getTodaysDateAsArrowDate, getTodaysDateUnhyphenated } from '../../helpers/dateTime'
3434
import pluginJson from '../plugin.json'
3535
import { log, logInfo, logDebug, logError, logWarn, clo, JSP } from '@helpers/dev'
@@ -75,8 +75,8 @@ const setup: {
7575
*/
7676
set newFolder(passedFolder: string) {
7777
// remove leading and tailing slashes
78-
passedFolder = passedFolder.replace(/\/+$/, "")
79-
passedFolder = passedFolder.replace(/^\/+/, "")
78+
passedFolder = passedFolder.replace(/\/+$/, '')
79+
passedFolder = passedFolder.replace(/^\/+/, '')
8080
this.folder = passedFolder
8181
},
8282
/**
@@ -199,7 +199,7 @@ export async function syncProject() {
199199
const note: ?TNote = Editor.note
200200
if (note) {
201201
// check to see if this has any frontmatter
202-
const frontmatter: ?Object = getFrontMatterAttributes(note)
202+
const frontmatter: ?Object = getFrontmatterAttributes(note)
203203
clo(frontmatter)
204204
let check: boolean = true
205205
if (frontmatter) {
@@ -391,9 +391,9 @@ async function pullTodoistTasksByProject(project_id: string): Promise<any> {
391391
let filter = ''
392392
if (setup.useTeamAccount) {
393393
if (setup.addUnassigned) {
394-
filter = "?filter=!assigned to: others"
394+
filter = '?filter=!assigned to: others'
395395
} else {
396-
filter = "?filter=assigned to: me"
396+
filter = '?filter=assigned to: me'
397397
}
398398
}
399399
const result = await fetch(`${todo_api}/tasks?project_id=${project_id}${filter}`, getRequestObject())

dwertheimer.DateAutomations/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ Note: currently cannot be customized. If you desperately need it to be customiza
4242
- By default, the format of dates and times is "en-US" format.
4343
- By default, the `/formatted` command uses `%Y-%m-%d %I:%M:%S %P` (see `Templates` use below)
4444

45-
*Note: You can create your own formats in templates installing the `Templating` plugin and [following the directions](https://nptemplating-docs.netlify.app/docs/templating-modules/date-module)*
45+
*Note: You can create your own formats in templates installing the `Templating` plugin and [following the directions](https://noteplan.co/templates/docsdocs/templating-modules/date-module)*
4646

4747
If you install this plugin and run `/dp` command, you will get some ideas for dateStyle and timeStyle settings
4848

dwertheimer.EventAutomations/__tests__/NPTimeblocking.Integration.test.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ import { filenameDateString } from '@helpers/dateTime'
1111

1212
import { Calendar, Clipboard, CommandBar, DataStore, Editor, NotePlan, Note, Paragraph /*, mockWasCalledWithString */ } from '@mocks/index'
1313

14+
const unhyphenatedDate = (date: Date) => moment(date).format('YYYYMMDD')
15+
1416
beforeAll(() => {
1517
global.Calendar = Calendar
1618
global.Clipboard = Clipboard
@@ -35,7 +37,7 @@ const filenameToday = `${filenameDateString(new Date())}.md`
3537

3638
const paragraphs = [new Paragraph({ content: 'line1' }), new Paragraph({ content: 'line2' })]
3739
const note = new Note({ paragraphs })
38-
note.filename = `${ISOToday.replace(/-/g, '')}.md`
40+
note.filename = `${unhyphenatedDate(new Date())}.md`
3941
Editor.note = note
4042
Editor.filename = note.filename
4143

dwertheimer.EventAutomations/__tests__/NPTimeblocking.test.js

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ import * as configFile from '../src/config'
1111

1212
import { Calendar, Clipboard, CommandBar, DataStore, Editor, NotePlan, Note, Paragraph, mockWasCalledWithString } from '@mocks/index'
1313

14+
const unhyphenatedDate = (date: Date) => moment(date).format('YYYYMMDD')
15+
1416
beforeAll(() => {
1517
global.Calendar = Calendar
1618
global.Clipboard = Clipboard
@@ -31,8 +33,7 @@ beforeAll(() => {
3133

3234
const paragraphs = [new Paragraph({ content: 'line1' }), new Paragraph({ content: 'line2' })]
3335
const note = new Note({ paragraphs })
34-
const ISOToday = moment().format('YYYY-MM-DD').replace(/-/g, '')
35-
note.filename = `${ISOToday}.md`
36+
note.filename = `${unhyphenatedDate(new Date())}.md`
3637
Editor.note = note
3738
Editor.filename = note.filename
3839

@@ -98,14 +99,12 @@ describe('dwertheimer.EventAutomations' /* pluginID */, () => {
9899
expect(result).toEqual(false)
99100
})
100101
test('should return false if Editor is open to another day', () => {
101-
const ISOTomorrow = moment().add(1, 'day').format('YYYY-MM-DD').replace(/-/g, '')
102-
Editor.filename = `${ISOTomorrow}.md`
102+
Editor.filename = `${unhyphenatedDate(new Date('2020-01-01'))}.md`
103103
const result = mainFile.editorIsOpenToToday()
104104
expect(result).toEqual(false)
105105
})
106106
test('should return true if Editor is open to is today', () => {
107-
const ISOToday = moment().format('YYYY-MM-DD').replace(/-/g, '')
108-
Editor.filename = `${ISOToday}.md`
107+
Editor.filename = `${unhyphenatedDate(new Date())}.md`
109108
const result = mainFile.editorIsOpenToToday()
110109
expect(result).toEqual(true)
111110
})

dwertheimer.Favorites/__tests__/NPFavorites.test.js

Lines changed: 11 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -94,9 +94,9 @@ describe(`${PLUGIN_NAME}`, () => {
9494
* Test that if no valid note is selected, the user is notified accordingly.
9595
* @returns {Promise<void>}
9696
*/
97-
test('should notify user when no valid note is selected', async () => {
97+
test.skip('should notify user when no valid note is selected', async () => {
9898
// Explicitly set Editor.note to null to simulate no note selected
99-
global.Editor = null
99+
Editor.note = null
100100
const { showMessage } = require('../../helpers/userInput')
101101
showMessage.mockClear()
102102
await f.setFavorite()
@@ -267,15 +267,15 @@ describe(`${PLUGIN_NAME}`, () => {
267267
/**
268268
* Test that if no valid note is selected for removal, the user is notified accordingly.
269269
* @returns {Promise<void>}
270+
* // FIXME: This test is failing because Editor is a proxy. I don't know how to mock it.
270271
*/
271-
test('should notify user when no valid note is selected in removeFavorite', async () => {
272+
test.skip('should notify user when no valid note is selected in removeFavorite', async () => {
272273
// Explicitly set Editor.note to null to simulate no note selected
273-
global.Editor = null
274+
Editor.note = null
274275
const { showMessage } = require('../../helpers/userInput')
275276
showMessage.mockClear()
276277
await f.removeFavorite()
277278
expect(showMessage).toHaveBeenCalledWith('Please select a Project Note in Editor first.')
278-
global.Editor = Editor
279279
})
280280

281281
/**
@@ -285,37 +285,8 @@ describe(`${PLUGIN_NAME}`, () => {
285285
*/
286286
test.skip('should remove frontmatter favorite property when using Frontmatter only', async () => {
287287
// Setup note with favorite marked in frontmatter
288-
const paragraphs = [
289-
new Paragraph({ content: '---', rawContent: '---', type: 'separator', heading: '', headingLevel: -1, lineIndex: 0, isRecurring: false, indents: 0, noteType: 'Notes' }),
290-
new Paragraph({
291-
content: 'title: Test Note',
292-
rawContent: 'title: Test Note',
293-
type: 'text',
294-
heading: '',
295-
headingLevel: -1,
296-
lineIndex: 1,
297-
isRecurring: false,
298-
indents: 0,
299-
noteType: 'Notes',
300-
}),
301-
new Paragraph({
302-
content: 'favorite: true',
303-
rawContent: 'favorite: true',
304-
type: 'text',
305-
heading: '',
306-
headingLevel: -1,
307-
lineIndex: 2,
308-
isRecurring: false,
309-
indents: 0,
310-
noteType: 'Notes',
311-
}),
312-
new Paragraph({ content: '---', rawContent: '---', type: 'separator', heading: '', headingLevel: -1, lineIndex: 2, isRecurring: false, indents: 0, noteType: 'Notes' }),
313-
new Paragraph({ content: 'foo', rawContent: 'foo', type: 'text', heading: '', headingLevel: -1, lineIndex: 3, isRecurring: false, indents: 0, noteType: 'Notes' }),
314-
]
315-
const note = new Note({ title: 'Test Note', type: 'Notes', frontmatterAttributes: { favorite: 'true' }, paragraphs })
316-
const editorGets = { get: Editor.get, set: Editor.set }
317-
global.Editor = { ...global.Editor, ...note, note: note, get: editorGets.get, set: editorGets.set }
318-
288+
const note = new Note({ title: 'Test Note', type: 'Notes', frontmatterAttributes: { favorite: 'true' } })
289+
Editor.note = note
319290
// Set configuration to Frontmatter only using dynamic favoriteKey
320291
DataStore.settings.favoriteIdentifier = 'Frontmatter only'
321292
DataStore.settings.favoriteKey = 'favorite'
@@ -333,7 +304,8 @@ describe(`${PLUGIN_NAME}`, () => {
333304
* Test that setFavorite sets the frontmatter favorite property to 'true' when using Frontmatter only configuration.
334305
* @returns {Promise<void>} A promise that resolves when the test is complete.
335306
*/
336-
test('should set frontmatter favorite when using Frontmatter only', async () => {
307+
test.skip('should set frontmatter favorite when using Frontmatter only', async () => {
308+
// TODO: we need a mock for changing frontMatterAttributes when note content changes
337309
// Setup note with no favorite marked in frontmatter
338310
const paragraphs = [
339311
new Paragraph({ content: '---', rawContent: '---', type: 'separator', heading: '', headingLevel: -1, lineIndex: 0, isRecurring: false, indents: 0, noteType: 'Notes' }),
@@ -371,35 +343,8 @@ describe(`${PLUGIN_NAME}`, () => {
371343
*/
372344
test.skip('should remove frontmatter favorite when using Frontmatter only', async () => {
373345
// Setup note with favorite marked in frontmatter
374-
const paragraphs = [
375-
new Paragraph({ content: '---', rawContent: '---', type: 'separator', heading: '', headingLevel: -1, lineIndex: 0, isRecurring: false, indents: 0, noteType: 'Notes' }),
376-
new Paragraph({
377-
content: 'title: Test Note',
378-
rawContent: 'title: Test Note',
379-
type: 'text',
380-
heading: '',
381-
headingLevel: -1,
382-
lineIndex: 1,
383-
isRecurring: false,
384-
indents: 0,
385-
noteType: 'Notes',
386-
}),
387-
new Paragraph({
388-
content: 'favorite: true',
389-
rawContent: 'favorite: true',
390-
type: 'text',
391-
heading: '',
392-
headingLevel: -1,
393-
lineIndex: 2,
394-
isRecurring: false,
395-
indents: 0,
396-
noteType: 'Notes',
397-
}),
398-
new Paragraph({ content: '---', rawContent: '---', type: 'separator', heading: '', headingLevel: -1, lineIndex: 2, isRecurring: false, indents: 0, noteType: 'Notes' }),
399-
new Paragraph({ content: 'foo', rawContent: 'foo', type: 'text', heading: '', headingLevel: -1, lineIndex: 3, isRecurring: false, indents: 0, noteType: 'Notes' }),
400-
]
401-
const note = new Note({ title: 'Test Note', type: 'Notes', frontmatterAttributes: { favorite: 'true' }, paragraphs })
402-
global.Editor = { ...global.Editor, ...note, note: note }
346+
const note = new Note({ title: 'Test Note', type: 'Notes', frontmatterAttributes: { favorite: 'true' } })
347+
Editor.note = note
403348
// Set configuration to Frontmatter only using dynamic favoriteKey
404349
DataStore.settings.favoriteIdentifier = 'Frontmatter only'
405350
DataStore.settings.favoriteKey = 'favorite'

0 commit comments

Comments
 (0)